前言
在刷b站时,偶然看到了一个up主制作的机械四足,于是突发奇想就做了一个简易版的,做的非常简单见谅。
效果展示
机械四爪展示视频
所需材料
1.一块stm32f103c8t6开发板
2.8个蓝色SG90经典180度舵机
3.一块iic通信的oled屏(大屏小屏都可以)
4.一块3.7v锂电池+一块5v锂电池 or 用我的懒蛋方法直接使用stlink供电
原理图和PCB设计说明
使用软件为嘉立创EDA(专业版)
已在嘉立创开源平台开源:基于stm32的机械四爪 - 嘉立创EDA开源硬件平台
这里就主要对代码进行说明。
代码部分说明
使用的keil5来进行编程,这里其实只需要编写一下PWM的文件,oled直接抄网上整理好的头文件就好了。
所用头文件总览
是的没错,只需要知道舵机和oled屏如何控制就可以进行制作,因为没有加其他蓝牙等模块,所以极其简单,适合我这种萌新做着玩。
PWM控制舵机部分
需要先查看stm32的TIM时钟表
我这里选用了TIM2和TIM4,所以要对PA0、PA1、PA2、PA3和PB6、PB7、PB8、PB9这几个IO口进行定义:
舵机控制的.h文件
#ifndef __DUOJI_H
#define __DUOJI_H
void Pwm_Init_TIM2(void);
void Pwm_Init_TIM4(void);
#endif
舵机控制的.c文件
#include "stm32f10x.h" // Device header
#include "duoji.h"
void Pwm_Init_TIM2(void)
{
GPIO_InitTypeDef GPIO_InitTypeStruce; //这里的名称我自己是喜欢定义很短的
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeStruce; //这里是看大佬们的定义方式定义的
TIM_OCInitTypeDef TIM_OCInitTypeStruce;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能GPIOA口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //使能TIM2定时器时钟
GPIO_InitTypeStruce.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitTypeStruce.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitTypeStruce.GPIO_Speed=GPIO_Speed_50MHz;
TIM_TimeBaseInitTypeStruce.TIM_Period=1999; //2000-1 arr
TIM_TimeBaseInitTypeStruce.TIM_Prescaler=719; //720-1 psc
//这里是算一个 20ms = (720*2000)/72000000=0.02
//TIM_TimeBaseInitTypeStruce.TIM_Period=199; //200-1
//TIM_TimeBaseInitTypeStruce.TIM_Prescaler=7199; //7200-1
//这样算也是 20ms = (7200*200)/72000000=0.02
TIM_TimeBaseInitTypeStruce.TIM_ClockDivision=0;
TIM_TimeBaseInitTypeStruce.TIM_CounterMode=TIM_CounterMode_Up;
TIM_OCInitTypeStruce.TIM_OCMode=TIM_OCMode_PWM1; //选择PWM模式1
TIM_OCInitTypeStruce.TIM_OutputState=TIM_OutputState_Enable; //比较输出使能
TIM_OCInitTypeStruce.TIM_OCPolarity=TIM_OCPolarity_High;
GPIO_Init(GPIOA,&GPIO_InitTypeStruce); //这里对所有的TIM2时钟的通道进行初始化
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitTypeStruce);
TIM_OC1Init(TIM2,&TIM_OCInitTypeStruce);
TIM_OC2Init(TIM2,&TIM_OCInitTypeStruce);
TIM_OC3Init(TIM2,&TIM_OCInitTypeStruce);
TIM_OC4Init(TIM2,&TIM_OCInitTypeStruce);
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_Cmd(TIM2,ENABLE);
}
void Pwm_Init_TIM4(void)
{
GPIO_InitTypeDef GPIO_InitTypeStruce;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitTypeStruce;
TIM_OCInitTypeDef TIM_OCInitTypeStruce;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //使能TIM4定时器时钟
GPIO_InitTypeStruce.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitTypeStruce.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitTypeStruce.GPIO_Speed=GPIO_Speed_50MHz;
TIM_TimeBaseInitTypeStruce.TIM_Period=1999; //arr
TIM_TimeBaseInitTypeStruce.TIM_Prescaler=719; //psc
TIM_TimeBaseInitTypeStruce.TIM_ClockDivision=0;
TIM_TimeBaseInitTypeStruce.TIM_CounterMode=TIM_CounterMode_Up;
TIM_OCInitTypeStruce.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitTypeStruce.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitTypeStruce.TIM_OCPolarity=TIM_OCPolarity_High;
GPIO_Init(GPIOB,&GPIO_InitTypeStruce); //对TIM4时钟对应的四个通道初始化
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitTypeStruce);
TIM_OC1Init(TIM4,&TIM_OCInitTypeStruce);
TIM_OC2Init(TIM4,&TIM_OCInitTypeStruce);
TIM_OC3Init(TIM4,&TIM_OCInitTypeStruce);
TIM_OC4Init(TIM4,&TIM_OCInitTypeStruce);
TIM_OC1PreloadConfig(TIM4,TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM4,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM4,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM4,TIM_OCPreload_Enable);
TIM_Cmd(TIM4,ENABLE);
}
对舵机控制时,我们就需要使用淘宝之力,搜出来舵机参数,下图就是淘宝里搜SG90,180度舵机的数据手册:
我们可以看出需要20ms的脉冲,对TIMx计数器产生的PWM计算中
TIM_TimeBaseInitTypeStruce.TIM_Period=arr;
TIM_TimeBaseInitTypeStruce.TIM_Prescaler=psc;
TIM_TimeBaseInitTypeStruce.TIM_ClockDivision=0;
TIM_TimeBaseInitTypeStruce.TIM_ClockDivision=0;
是重复计数器,高级定时器才有,不需要用所以直接给0关闭就好了。
TIM_TimeBaseInitTypeStruce.TIM_Period=arr;
TIM_TimeBaseInitTypeStruce.TIM_Prescaler=psc;
arr和psc这两个参数决定我们的定时时间。
arr是计数器的自动重装值,也就是执行中断的总次数,可以确定定时器溢出的时间间隔
psc是预分频器,将系统时钟分频后作为定时器的时钟源,TIM时钟的总线时钟最大为72MHz(CK_psc)。
时间计算公式
这里的CK_psc就是72MHz,我们就可以算出很多组时间等于20ms的数据
arr==200-1;psc==7200-1; //这样就是以比较低的频率计比较少的数
arr==2000-1;psc==720-1; //这样就是以比较高的频率计比较多的数
这些数据算出来,理论上都可以直接使用,只是arr和psc的值会影响TIM的计数速度和计数周期。
arr的值还会影响我们接下来的ccr占空比控制。
但是要注意arr和psc取值都要在0~65535之间。
编写完PWM初始化的文件后我们就可以使用ccr来控制占空比,控制旋转的范围。
TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
这几个函数进行占空比控制,这里1234对应内部选择的TIMx时钟的第几个通道。
同样我们还是离不开TIM时钟表
eg:
TIM_SetCompare3(TIM2,50);//这里意味着TIM2时钟上的第三个串口PA2,给ccr值50
TIM_SetCompare2(TIM4,250);//这里意味着TIM4时钟上的第二个串口PB7,给ccr值250
用到了PWM占空比公式
因为我选用的arr为1999+1,所以:
这里的50对应的占空比就是50/2000,20ms的1/40对应的就是0.5ms舵机转到0°。
而这里的250对应占空比就是250/2000,20ms的1/8对应2.5ms舵机转到180°。
那么舵机旋转角度就是50~250对应0°~180°。
到这里,最关键的东西已经结束了,剩下的就是对每个舵机进行控制即可。
(新手教程结束,你可以对线theshy、做掉克列了)
比如我下面主函数中定义的各种动作:
eg:
main.c
#include "stm32f10x.h" // Device header
#include "oled.h"
#include "Delay.h"
#include "duoji.h"
void move(void);
void move2(void);
void move3(void);
void move4(void);
void zuoyou(void);
void zhaoshou(void);
void zhanli(void);
void bailan(void);
int main()
{
Pwm_Init_TIM2();
Pwm_Init_TIM4();
OLED_Init(); //初始化OLED
OLED_ShowString(46,20,(u8*)"0.0",24);
OLED_Refresh();
Delay_ms(50);
while(1)
{
zhanli();
Delay_ms(500);
zhaoshou();
zhanli();
Delay_ms(500);
zuoyou();
zhanli();
Delay_ms(500);
bailan();
}
}
void move(void)
{
TIM_SetCompare3(TIM2,130);
TIM_SetCompare4(TIM2,130);
TIM_SetCompare3(TIM4,240);
TIM_SetCompare4(TIM4,240);
}
void move2(void)
{
TIM_SetCompare3(TIM2,60);
TIM_SetCompare4(TIM2,60);
TIM_SetCompare3(TIM4,170);
TIM_SetCompare4(TIM4,170);
}
void move3(void)
{
TIM_SetCompare1(TIM2,120);
TIM_SetCompare2(TIM2,180);
TIM_SetCompare1(TIM4,180);
TIM_SetCompare2(TIM4,120);
}
void move4(void)
{
TIM_SetCompare1(TIM2,180);
TIM_SetCompare2(TIM2,120);
TIM_SetCompare1(TIM4,120);
TIM_SetCompare2(TIM4,180);
}
void zuoyou(void)
{
u8 i;
for(i=0;i<5;i++)
{
OLED_ShowString(46,20,(u8*)"0.0",24);
OLED_Refresh();
Delay_ms(50);
move();
Delay_ms(500);
OLED_ShowString(46,20,(u8*)">.<",24);
OLED_Refresh();
Delay_ms(50);
move2();
Delay_ms(500);
}
}
void zhaoshou(void)
{
u8 i;
OLED_ShowString(46,20,(u8*)"^v^",24);
OLED_Refresh();
Delay_ms(50);
TIM_SetCompare1(TIM2,100);
Delay_ms(100);
TIM_SetCompare2(TIM2,190);
Delay_ms(100);
TIM_SetCompare3(TIM2,150);
Delay_ms(100);
TIM_SetCompare4(TIM2,80);
Delay_ms(100);
TIM_SetCompare2(TIM4,120);
Delay_ms(100);
for(i=0;i<3;i++)
{
TIM_SetCompare4(TIM4,150);
Delay_ms(300);
TIM_SetCompare4(TIM4,80);
Delay_ms(300);
}
}
void zhanli(void)
{
TIM_SetCompare1(TIM2,140);
Delay_ms(100);
TIM_SetCompare2(TIM2,150);
Delay_ms(100);
TIM_SetCompare1(TIM4,160);
Delay_ms(100);
TIM_SetCompare2(TIM4,150);
Delay_ms(100);
TIM_SetCompare3(TIM2,70);
Delay_ms(100);
TIM_SetCompare4(TIM2,70);
Delay_ms(100);
TIM_SetCompare3(TIM4,230);
Delay_ms(100);
TIM_SetCompare4(TIM4,230);
Delay_ms(100);
TIM_Cmd(TIM2,DISABLE);
Delay_ms(250);
TIM_Cmd(TIM2,ENABLE);
Delay_ms(250);
}
void bailan(void)
{
u8 i;
OLED_ShowString(46,20,(u8*)"X.X",24);
OLED_Refresh();
Delay_ms(50);
TIM_SetCompare3(TIM2,150);
Delay_ms(100);
TIM_SetCompare4(TIM2,150);
Delay_ms(100);
TIM_SetCompare3(TIM4,150);
Delay_ms(100);
TIM_SetCompare4(TIM4,150);
Delay_ms(100);
for(i=0;i<5;i++)
{
move3();
Delay_ms(300);
move4();
Delay_ms(300);
}
}
OLED部分的使用
请大家去CSDN的那些大佬哪里学一下使用方法吧
吃透OLED显示原理——玩转OLED模块各种使用方法_oled使用方法-CSDN博客
建模部分说明和参数
我使用的是solidworks 2022版本
舵机留的孔大小:
经过3d打印后的材料挤压,可以直接用2mm螺丝
脚留的孔大小:
同样用2mm螺丝即可
腿部连接:
因为用了镜像,在自己切片时需要进行一下切割。
安装注意事项
需要知道舵机的三条线:
黄线 or 橙线是信号线
红线是电源线
黑线 or 棕线是地线
首先需要知道舵机是如何旋转的:
然后在安装之前需要用代码将舵机都旋转到90°后再进行脚的固定,可以增大脚左右移动的自由度:
先用这几个代码
TIM_SetCompare1(TIM2,150);
TIM_SetCompare2(TIM2,150);
TIM_SetCompare3(TIM2,150);
TIM_SetCompare4(TIM2,150);
TIM_SetCompare1(TIM4,150);
TIM_SetCompare2(TIM4,150);
TIM_SetCompare3(TIM4,150);
TIM_SetCompare4(TIM4,150);
将舵机调至90°后,按下图连接腿部。
如果要用我的代码进行运行,需要如下图连接舵机线:
总结
源码、sw源文件和3d打印文件都在嘉立创的开源平台。
因为我也还是一个大学的萌新,所以做的很简单,有问题也请大家指出,谢谢。