两轮平衡小车制作保姆式教程(2-2)——软件模块:直流编码器电机的使用

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

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

📃个人主页:麦克斯科技

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

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

前言

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

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

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

编码器和电机

编码器和电机的底层程序,其实就是STM32定时器编码器模式与TB6612的使用,非常简单,这里给出STM32F103的范例程序。

我的资源分配方案如下:
编码器1–PA0、PA1 TIM5 右
编码器2–PB6、PB7 TIM4 左
PWMA:B0
PWMB: B1
AIN1:PB12
AIN2:PB13
BIN1:PB14
BIN2:PB15

hal_encoder.c

//**********************编码器时钟初始化*********************
void Encoder_Count_RCC(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
}
//**********************编码器引脚初始化*********************
void Encoder_Count_GPIO(void)
{GPIO_InitTypeDef GPIO_InitStruct;//**********TIM4,B6,B7****************GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);//**********TIM5,A0,A1****************GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);}
//**********************编码器功能初始化*********************
void Encoder_Count_Configuration(void)
{TIM_ICInitTypeDef TIM_ICInitStruct;TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//**********TIM4,B6,B7***********************************TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitStruct.TIM_Period=65535;    //65536-1TIM_TimeBaseInitStruct.TIM_Prescaler=0;     //1-1TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);	TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;TIM_ICInitStruct.TIM_ICFilter=0xF;TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;TIM_ICInit(TIM4, &TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;TIM_ICInitStruct.TIM_ICFilter=0xF;TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;TIM_ICInit(TIM4, &TIM_ICInitStruct);	TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Falling,TIM_ICPolarity_Rising);TIM_Cmd(TIM4,ENABLE);  //**********TIM5,A0,A1***********************************TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitStruct.TIM_Period=65535;    //65536-1TIM_TimeBaseInitStruct.TIM_Prescaler=0;     //1-1TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct);	TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;TIM_ICInitStruct.TIM_ICFilter=0xF;TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;TIM_ICInit(TIM5, &TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;TIM_ICInitStruct.TIM_ICFilter=0xF;TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;TIM_ICInit(TIM5, &TIM_ICInitStruct);	TIM_EncoderInterfaceConfig(TIM5,TIM_EncoderMode_TI12,TIM_ICPolarity_Falling,TIM_ICPolarity_Rising);TIM_Cmd(TIM5,ENABLE);  
}
//**********************编码器初始化*********************
void Encoder_Count_Init(void)
{Encoder_Count_RCC();Encoder_Count_GPIO();Encoder_Count_Configuration();
}
//******************编码器数据读取********************************
int Encoder_Value(TIM_TypeDef* TIMx)
{ int channal_val=0;channal_val = TIMx ->CNT;if(channal_val>>15){			channal_val =  (channal_val&0x7FFF)-32767;}	return channal_val;
}
//****************编码器清零*************************************
void Encoder_Count_Clear(TIM_TypeDef* TIMx)
{TIMx ->CNT = 0;
}

hal_encoder.h

#ifndef _HAL_ENCODER_H
#define _HAL_ENCODER_H#include "stm32f10x.h"
//**************************************************
void Encoder_Count_RCC(void);
void Encoder_Count_GPIO(void);
void Encoder_Count_Configuration(void);
void Encoder_Count_Init(void);
int Encoder_Value(TIM_TypeDef* TIMx);
void Encoder_Count_Clear(TIM_TypeDef* TIMx);#endif

hal_pwm.c

/***
配置PWM通道 产生PWM 一个tb6612可以同时驱动两路电机
***///**********************配置系统时钟*********************************
void PWM_RCC(void)
{ //使能GPIO外设(PWM引脚B0 B1 B4 B5 时钟配置)和AFIO复用功能模块时钟  B4 B5是JTDO下载引脚RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);     //打开time3的中断时钟GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5   
}
//**********************配置GPIO管脚****************B0 B1 //B4 B5//***************
void PWM_GPIO(void)
{GPIO_InitTypeDef GPIO_InitStructure;//PWM管脚PWM1配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);//PWM管脚PWM2配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);driver_pin_init();}
//**********************时钟中断配置函数*********************************
void PWM_TIM3_Configuration(void)
{ TIM_OCInitTypeDef  TIM_OCInitStructure;                      //定义结构体变量TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  //GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,DISABLE);         //关闭映射功能   TIM_TimeBaseStructure.TIM_Period=100;                       //计数100,PWM频率10KHz  TIM_TimeBaseStructure.TIM_Prescaler=72;                       //不分频TIM_TimeBaseStructure.TIM_ClockDivision=0;                   //不滤波TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;    //向上计数模式TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);               //初始化TIM_Cmd(TIM3,ENABLE);                                        //打开定时器外设
//***配置PWM1**********	
//  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;            //PWM模式1
//  TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;  //打开PWM使能
//  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;     //输出极性
//	TIM_OC1Init(TIM3, & TIM_OCInitStructure); 	                 //初始化  使用通道1
//	TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);             //打开PWM中断使能,否则只能执行一次
	
	//***配置PWM2**********	
//  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;            //PWM模式1
//  TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;  //打开PWM使能
//  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;     //输出极性
//	TIM_OC2Init(TIM3, & TIM_OCInitStructure); 	                 //初始化  使用通道2
//	TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);             //打开PWM中断使能,否则只能执行一次
//	
	//***配置PWM3**********	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;            //PWM模式1TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;  //打开PWM使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;     //输出极性TIM_OC3Init(TIM3, & TIM_OCInitStructure); 	                 //初始化  使用通道3TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);             //打开PWM中断使能,否则只能执行一次
//	
//	//***配置PWM4**********	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;            //PWM模式1TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;  //打开PWM使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;     //输出极性TIM_OC4Init(TIM3, & TIM_OCInitStructure); 	                 //初始化  使用通道4TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);             //打开PWM中断使能,否则只能执行一次}
//**************************配置优先级***********************************
void TIM3_NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;                        //为结构体定义结构体变量NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);             //对优先级进行分组NVIC_InitStructure.NVIC_IRQChannel =TIM3_IRQn;              NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   //抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          //响应优先级为0         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能NVIC_Init(&NVIC_InitStructure);                             //初始化
}
//**********************PWM初始化函数*********************************
void PWM_Init(void)
{PWM_RCC();                  //PWM时钟配置   PWM_GPIO();                 //PWM管脚配置PWM_TIM3_Configuration();   //占空比时钟控制 TIM3_NVIC_Configuration();  //优先级配置TIM_SetCompare3(TIM3,0);    //防止上电就乱动  PB0TIM_SetCompare4(TIM3,0);    //防止上电就乱动  PB1NEncoder.left_motor_dir = 1;NEncoder.right_motor_dir = 1;}
/***驱动引脚配置 PB12 13 14 15***/
void driver_pin_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE); 						 GPIO_InitStructure.GPIO_Pin = AIN1_PIN | AIN2_PIN | BIN1_PIN | BIN2_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure);L_MOTOR_GO;	R_MOTOR_GO;
}

hal_pwm.h

#ifndef _HAL_PWM_H
#define _HAL_PWM_H
#include "stm32f10x.h"#define AIN1_PIN GPIO_Pin_12
#define AIN2_PIN GPIO_Pin_13
#define BIN1_PIN GPIO_Pin_14
#define BIN2_PIN GPIO_Pin_15
//电机初始化相关函数
void PWM_RCC(void);                  //时钟配置   
void PWM_GPIO(void);                 //管脚配置
void PWM_TIM3_Configuration(void);   //占空比时钟控制
void TIM3_NVIC_Configuration(void);  //优先级
void PWM_Init(void);                 //初始化 
void driver_pin_init(void);#endif 

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



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

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]