两轮平衡小车制作保姆式教程(2-1)——软件模块:JY62

2024-01-27 10:36

本文主要是介绍两轮平衡小车制作保姆式教程(2-1)——软件模块:JY62,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

✅作者简介:大家好我是:麦克斯科技,希望一起努力,一起进步!

📃个人主页:麦克斯科技

🔥系列专栏:两轮平衡小车制作保姆式教程
🏷️非常欢迎大家在评论区留言交流,互相学习!

提前声明:博客中给出的代码经过多个项目测试,实测能用,性能稳定,请大家放心使用!

前言

本系列博客将从硬件到软件详细介绍“如何制作一辆两轮自平衡小车”,笔者毫无保留,以最通俗易懂的语言,以最简单的实现方案,分享自己从0到1制作平衡小车的全过程,相信跟着我的教程,大家也能顺利制作一台属于自己的平衡车。系列专栏:🔥两轮平衡小车制作保姆式教程🔥

首先,给大家提前交个底,其实制作一台平衡小车并不难,用到的主要模块就是陀螺仪,而最主要的控制算法就是PID算法,而且平衡小车对陀螺仪与PID算法的掌握程度要求并不是很高,所以适合初学者来作为项目练手。

该系列教程一共分为4个板块,分为《硬件选型》、《软件模块》、《直立环、速度环、转向环》、《调参保姆级教程》,4个板块条理清晰,层次分明,简明扼要,请大家跟着我开始学习吧!

软件模块

软件主要模块:陀螺仪、编码器、0.96寸OLED显示屏、电机、直立环、速度环、转向环等。

陀螺仪JY62

这里我用钱来解决了麻烦的问题,重金入手了一个性能较好的六轴陀螺仪jy62。只需要使用单片机的一个串口,这个模块对数据的处理已经做的很成功了。

我使用的主控是TM4C123,我也会给出使用STM32的代码,两个芯片是同一个处理思路,只是两个芯片的底层函数不一样。照着我的文件形式,建立imu.c与imu.h或者mt_jy62.c与mt_jy62.c,复制粘贴我的代码,根据实际情况修改,绝对能用,我在电赛和课设都是用的这个代码,实测真好用!

TM4C123的JY62底层程序

imu.c

//
// Created by 麦克斯科技.
//
#include "platform/platform.h"static uint8_t buffer[11];
imu_data_t acceleration, gyroscope, angle;// uart1
void imu_init()
{SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);GPIOPinConfigure(GPIO_PB0_U1RX);GPIOPinConfigure(GPIO_PB1_U1TX);GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0);GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_1);UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 115200, UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE|UART_CONFIG_PAR_NONE);UARTFIFOEnable(UART1_BASE);UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX2_8, UART_FIFO_RX2_8);UARTIntEnable(UART1_BASE, UART_INT_RX|UART_INT_RT);void imu_handler(void);UARTIntRegister(UART1_BASE, imu_handler);IntPrioritySet(INT_UART1, USER_INT2);IntEnable(INT_UART1);IntMasterEnable();UARTEnable(UART1_BASE);
}static uint8_t get_verify_code()
{uint32_t sum = 0;for (uint8_t i = 0; i < 10; ++i) {sum += buffer[i];}return sum&0xff;
}int32_t k = 0;
void imu_analysis()
{static float last_angle_z = 0, angle_z = 0;switch (buffer[1]) {case 0x51:acceleration.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*16;acceleration.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*16;acceleration.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*16;break;case 0x52:gyroscope.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*2000;gyroscope.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*2000;gyroscope.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*2000;break;case 0x53:angle.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*180;angle.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*180;angle_z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*180;break;}if (last_angle_z < -90 && angle_z > 90)k --;if (last_angle_z > 90 && angle_z < -90)k ++;last_angle_z = angle_z;
//    printf("k:%ld\n", k);angle.z = angle_z + (float )k*360;
}#define ERROR   0
#define DOING   1
#define SUCCESS 2
static uint8_t n = 0, state = 0;
void imu_handler()
{uint32_t data;uint32_t status=UARTIntStatus(UART1_BASE, true);UARTIntClear(UART1_BASE, status);while(UARTCharsAvail(UART1_BASE)){data = UARTCharGetNonBlocking(UART1_BASE)&0xff;//	data = UARTCharGet(UART1_BASE)&0xff;//	UARTCharPutNonBlocking(UART0_BASE, data);//	printf("data:%d\n", data);if (state == ERROR){n = 0;}buffer[n] = (uint8_t)data;if (n == 0){if (buffer[0] == 0x55){state = DOING;} else{state = ERROR;}} else if (n == 10){if (buffer[10] == get_verify_code()){state = SUCCESS;} else{state = ERROR;}}n += 1;if (state == SUCCESS){
//			printf("OK\n");imu_analysis();state = ERROR;}}
}

imu.h

//
// Created by 麦克斯科技.
//
#ifndef IMU_H
#define IMU_Htypedef struct {float x;float y;float z;
}imu_data_t;void imu_init(void);
extern imu_data_t acceleration, gyroscope, angle;
#endif /* IMU_H */

STM32F103的JY62底层程序

mt_jy62.c

//
// Created by 麦克斯科技.
//
#include "mt_jy62.h"
#include <string.h>
#include <stdio.h>
#include "hal_usart.h"
#include "mt_pid.h"static int32_t n = 0 ;
int32_t state = 0;
static uint8_t buffer[11];
imu_data_t acceleration, gyroscope, angle; 
int32_t k = 0;
char analysis_flag = 0;
void imu_analysis(void)
{static float last_angle_z = 0, angle_z = 0;switch (buffer[1]) {case 0x51:acceleration.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*16;acceleration.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*16;acceleration.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*16;break;case 0x52:gyroscope.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*2000;gyroscope.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*2000;gyroscope.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*2000;break;case 0x53:angle.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*180;angle.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*180;angle_z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*180;break;}if (last_angle_z < -90 && angle_z > 90){//	k = -1;k--;}if (last_angle_z > 90 && angle_z < -90){ //	k = 1;k++;}last_angle_z = angle_z;angle.z = angle_z + k*360;}static uint8_t get_verify_code()
{uint32_t sum = 0;for (uint8_t i = 0; i < 10; ++i) {sum += buffer[i];}return sum&0xff;
}
void USART1_IRQHandler(void)                  //串口3中断服务程序
{uint32_t data;if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET){							data =  USART_ReceiveData(USART1)&0xff;if (state == ERROR){n = 0;}buffer[n] = (uint8_t)data;if (n == 0){if (buffer[0] == 0x55){state = DOING;} else{state = ERROR;}}else if (n == 10){if (buffer[10] == get_verify_code()){state = SUCCESS;} else{state = ERROR;}}
//		else
//		{
//			state = ERROR;
//		}n += 1;if (state == SUCCESS){imu_analysis();//analysis_flag = 1;state = ERROR;}else{analysis_flag = 0;}USART_ClearITPendingBit(USART1,USART_IT_RXNE);}	}

mt_jy62.h

//
// Created by 麦克斯科技.
//
#ifndef _MT_JY62_H
#define _MT_JY62_H#include "stm32f10x.h"
//**************************************************
typedef struct {int x;int y;int z;
}imu_data_t;#define ERROR   0
#define DOING   1
#define SUCCESS 2void imu_init(void);
void imu_analysis(void);
extern imu_data_t acceleration, gyroscope, angle;
//extern int32_t state;
extern char analysis_flag;#endif

代码分析

按函数分析

void imu_init(); //imu初始化

陀螺仪的初始化,初始化引脚,注册串口中断函数,打开中断。

void imu_init()
{SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);GPIOPinConfigure(GPIO_PB0_U1RX);GPIOPinConfigure(GPIO_PB1_U1TX);GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0);GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_1);UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 115200, UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE|UART_CONFIG_PAR_NONE);UARTFIFOEnable(UART1_BASE);UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX2_8, UART_FIFO_RX2_8);UARTIntEnable(UART1_BASE, UART_INT_RX|UART_INT_RT);void imu_handler(void);UARTIntRegister(UART1_BASE, imu_handler);IntPrioritySet(INT_UART1, USER_INT2);IntEnable(INT_UART1);IntMasterEnable();UARTEnable(UART1_BASE);
}
static uint8_t get_verify_code();//数据校准

10个为一组,对串口获取到的 buffer[i]数据进行求和。

static uint8_t get_verify_code()
{uint32_t sum = 0;for (uint8_t i = 0; i < 10; ++i) {sum += buffer[i];}return sum&0xff;
}
void imu_analysis(); //数据分析

对陀螺仪获取到的数据进行处理,得到角速度和角度。

其中有一个对角度的巧妙处理,是的偏航角以z轴作为0度分界,解决陀螺仪自身得到角度的不连续性。

    if (last_angle_z < -90 && angle_z > 90)k --;if (last_angle_z > 90 && angle_z < -90)k ++;last_angle_z = angle_z;
//    printf("k:%ld\n", k);angle.z = angle_z + (float )k*360;
int32_t k = 0;
void imu_analysis()
{static float last_angle_z = 0, angle_z = 0;switch (buffer[1]) {case 0x51:acceleration.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*16;acceleration.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*16;acceleration.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*16;break;case 0x52:gyroscope.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*2000;gyroscope.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*2000;gyroscope.z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*2000;break;case 0x53:angle.x = (float )((int16_t)(buffer[2]|(buffer[3]<<8)))/32768*180;angle.y = (float )((int16_t)(buffer[4]|(buffer[5]<<8)))/32768*180;angle_z = (float )((int16_t)(buffer[6]|(buffer[7]<<8)))/32768*180;break;}if (last_angle_z < -90 && angle_z > 90)k --;if (last_angle_z > 90 && angle_z < -90)k ++;last_angle_z = angle_z;
//    printf("k:%ld\n", k);angle.z = angle_z + (float )k*360;
}
void imu_handler(); //中断回调函数

处理中断工作。在这里面调用imu_analysis()函数。

#define ERROR   0
#define DOING   1
#define SUCCESS 2
static uint8_t n = 0, state = 0;
void imu_handler()
{uint32_t data;uint32_t status=UARTIntStatus(UART1_BASE, true);UARTIntClear(UART1_BASE, status);while(UARTCharsAvail(UART1_BASE)){data = UARTCharGetNonBlocking(UART1_BASE)&0xff;//	data = UARTCharGet(UART1_BASE)&0xff;//	UARTCharPutNonBlocking(UART0_BASE, data);//	printf("data:%d\n", data);if (state == ERROR){n = 0;}buffer[n] = (uint8_t)data;if (n == 0){if (buffer[0] == 0x55){state = DOING;} else{state = ERROR;}} else if (n == 10){if (buffer[10] == get_verify_code()){state = SUCCESS;} else{state = ERROR;}}n += 1;if (state == SUCCESS){
//			printf("OK\n");imu_analysis();state = ERROR;}}
}

这篇关于两轮平衡小车制作保姆式教程(2-1)——软件模块:JY62的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/649978

相关文章

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

springboot简单集成Security配置的教程

《springboot简单集成Security配置的教程》:本文主要介绍springboot简单集成Security配置的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录集成Security安全框架引入依赖编写配置类WebSecurityConfig(自定义资源权限规则

MySQL Workbench 安装教程(保姆级)

《MySQLWorkbench安装教程(保姆级)》MySQLWorkbench是一款强大的数据库设计和管理工具,本文主要介绍了MySQLWorkbench安装教程,文中通过图文介绍的非常详细,对大... 目录前言:详细步骤:一、检查安装的数据库版本二、在官网下载对应的mysql Workbench版本,要是

通过Docker Compose部署MySQL的详细教程

《通过DockerCompose部署MySQL的详细教程》DockerCompose作为Docker官方的容器编排工具,为MySQL数据库部署带来了显著优势,下面小编就来为大家详细介绍一... 目录一、docker Compose 部署 mysql 的优势二、环境准备与基础配置2.1 项目目录结构2.2 基

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修

Python使用date模块进行日期处理的终极指南

《Python使用date模块进行日期处理的终极指南》在处理与时间相关的数据时,Python的date模块是开发者最趁手的工具之一,本文将用通俗的语言,结合真实案例,带您掌握date模块的六大核心功能... 目录引言一、date模块的核心功能1.1 日期表示1.2 日期计算1.3 日期比较二、六大常用方法详

最新Spring Security实战教程之Spring Security安全框架指南

《最新SpringSecurity实战教程之SpringSecurity安全框架指南》SpringSecurity是Spring生态系统中的核心组件,提供认证、授权和防护机制,以保护应用免受各种安... 目录前言什么是Spring Security?同类框架对比Spring Security典型应用场景传统