小车用的是平衡小车之家的,单片机用的是正点原子的STM32mini板。
舵机工作原理简单来说就是接收1个控制脉冲并驱动电机转动来控制方向,该脉冲的宽度决定舵机转动的角度。一般采用单片机来产生PWM信号来控制舵机,PWM波的周期为20ms,通过改变其占空比来改变其转动方向,具体如下图所示。我是采用PA8端口的PWM波来控制舵机的,PWM波初始化代码如下:
控制舵机的PWM波初始化
void TIM1_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能
//设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn ; //TIM5中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM1, ENABLE); //使能TIM1
}
再main函数中用TIM8_PWM_Init(199,7199);来产生周期20ms的PWM波,采用的PWM波为模式2且高电平为有效电平,所以舵机从-90°到90°转动时对应的CRR为175到195。
驱动模块使用的是A4950,其工作电压范围在7.6V到30V间。
其中VM为驱动模块的电源,接7.6V到40V,VCC接5V,GND接地。
其余引脚接法如下,PA8接舵机,PC6,7,8,9接AIN1,BIN1,BIN2,AIN2,BOUT1接电机线1+,BOUT2接电机线1-,AOUT1接电机线2+,AOUT2接电机线2-
控制原理就是AIN1和AIN2信号的压差来控制电机的转动速度与正转反转。例如AIN1接低电平(占空比为0,)AIN2接50%占空比的PWM波,电机以50%的速度转动。压差越大转速越快,压差相等则停转。
初始化代码如下
void TIM8_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE); //使能GPIO外设时钟使能
//设置该引脚为复用输出功能,输出TIM8 CH1的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_IRQn ; //TIM5中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM8, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM8, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC3Init(TIM8, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC4Init(TIM8, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_CtrlPWMOutputs(TIM8,ENABLE); //MOE 主输出使能
TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Enable); //CH1预装载使能
TIM_ARRPreloadConfig(TIM8, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM8, ENABLE); //使能TIM8
}
使用较为简单,IN+和IN-接2s航模电池,然后其可以输出5V,3.3V电压,以及航模电池本身电压。
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "pwm.h"
#include "remote.h"
#include "usart.h"
int main(void)
{ u8 sign=1;
u8 key;
u8 t=0;
u16 pwm1=185;
delay_init(); //延时函数初始?
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
uart_init(9600); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
TIM8_PWM_Init(199,7199);
TIM1_PWM_Init(199,7199);
Remote_Init(); //红外接收初始化
TIM_SetCompare2(TIM8,1);//1
TIM_SetCompare3(TIM8,1);
TIM_SetCompare1(TIM8,1);
TIM_SetCompare4(TIM8,1);
TIM_SetCompare1(TIM1,185);
while(1)
{
key=Remote_Scan();
if(key)
{
switch(key)
{
/*case 104:TIM_SetCompare1(TIM8,195);delay_ms(500);break; //1
case 152:TIM_SetCompare1(TIM1,175);break; //2*/
/*case 0:str="ERROR";break;
case 162:str="POWER";break;
case 98:str="UP";break;
case 2:str="PLAY";break;
case 226:str="ALIENTEK";break;
case 194:str="RIGHT";break;
case 34:str="LEFT";break;
case 224:str="VOL-";break;
case 168:str="DOWN";break;
case 144:str="VOL+";break; */
case 0:;break;
case 98:TIM_SetCompare1(TIM1,185);break;//up
case 194:TIM_SetCompare1(TIM1,175);break; //right
case 34:
TIM_SetCompare1(TIM1,195);break;//left
case 168:TIM_SetCompare2(TIM8,195);
TIM_SetCompare3(TIM8,175);
TIM_SetCompare1(TIM8,175);
TIM_SetCompare4(TIM8,195);
break; //down
case 2:TIM_SetCompare2(TIM8,1);//play
TIM_SetCompare3(TIM8,1);
TIM_SetCompare1(TIM8,1);
TIM_SetCompare4(TIM8,1);
break;
case 104:TIM_SetCompare2(TIM8,175);//1蜗牛模式
TIM_SetCompare3(TIM8,195);
TIM_SetCompare1(TIM8,195);
TIM_SetCompare4(TIM8,175);
delay_ms(500);
break;
case 152:TIM_SetCompare2(TIM8,125);//2正常模式
TIM_SetCompare3(TIM8,175);
TIM_SetCompare1(TIM8,175);
TIM_SetCompare4(TIM8,125);
break;
case 176:TIM_SetCompare2(TIM8,95);//3,究极超高速模式
TIM_SetCompare3(TIM8,195);
TIM_SetCompare1(TIM8,195);
TIM_SetCompare4(TIM8,95);
break;
/*case 152:str="2";break;
case 176:str="3";break;
case 48:str="4";break;
case 24:str="5";break;
case 122:str="6";break;
case 16:str="7";break;
case 56:str="8";break;
case 90:str="9";break;
case 66:str="0";break;
case 82:str="DELETE";break; */
}
}else delay_ms(10);
t++;
if(t==20)
{
t=0;
LED1=!LED1;
}
}
}
红外遥控的初始化代码参考正点原子,遥控器也是正点原子的,按键功能如下:play为暂停按键,down为后退,up为调正舵机角度,right为右转,left为左转,1为蜗牛向前,2为正常模式,3为究极高速模式。
当时脑子一热花300大洋买了小车组件,在学校一直没时间装,寒假有空花了2天时间搞了起来,还烧坏了一个mini板…不懂为啥我的稳压模块5V输出似乎有些不灵,第一天搞了半天舵机用稳压模块的5V不得转,用板子上5V输出就能转,后来干脆直接用稳压模块的7.6V的电压来控制舵机(做成之后没过多久舵机就烧坏了…,但好歹也做起来了)这是我第一次尝试用单片机做一个具体的东西,而不是就烧烧例程,讲真的花了挺久的,但起码还算是做成了…。还买了蓝牙模块和编码器…但给的上位机软件是安卓的,手机没法装,就暂时没整这些,等以后如果从事自控方向的话再说吧。(原本寒假要留校参加电赛冬令营的,没想到整这么一出,而且我选的是模拟方向,寒假头几天看完了你好放大器,看了会儿模电,线上冬令营极其不靠谱,队友也是别的班人找我的,没啥交流…感觉巨无聊,毕竟在家又没示波器等等工具…光看这些虚的不用很快就会忘了,不如亲手做个小车玩玩),哦,对了记得要所有模块都要共地呀。
附上演示视频:
32单片机控制的舵机小车的演示视频,点击文字即可查看