STM32实现舵机任意角度转动,详解不懂部分,薇哥帮你解答
一、准备
硬件:stm32任意型号,这里我使用stm32f103c8t6,sg90舵机,电位器。
软件:keil上号!
目标:使用梅花柄电位器控制舵机任意角度旋转。
二、相关资料
2.1 舵机驱使方法
舵机是一种根据输入PWM信号占空比来控制输出角度的装置
输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
stm32f1标准库PWM驱动的配置代码
void PWM_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//打开定时器晶振
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //GPIOA口晶振使能
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //模拟ad口
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
TIM_InternalClockConfig(TIM3); //选择TIM3内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//配置时基单元,建立结构体,配置参数,初始化单元
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//一分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInitStructure.TIM_Period =18000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 80 - 1; //PSC计数时间=晶振频率(72M)/ARR+1/PSC+1;计1s则为1hz;PSC越低频率越高
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure; //比较单元的结构体
TIM_OCStructInit(&TIM_OCInitStructure); //初始化结构体里所有的值,防止出现小问题
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM1模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //有效电平为高电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能
TIM_OCInitStructure.TIM_Pulse = 0; //CCR
TIM_OC3Init(TIM3, &TIM_OCInitStructure); //初始化
//TIM_OC2Init(TIM3, &TIM_OCInitStructure);
//PWM频率=晶振(72MHZ)/(ARR+1)/(PSC+1)
//PWM占空比=CCR/(ARR+1)
//PWM分辨率=1/(ARR+1)
TIM_Cmd(TIM3, ENABLE);//开启计数器使能
}
这里使用TIM3定时器,过程:先配置时基单元,再配置输出比较(PWM),对于计数值(ARR)和分频系数(PSC)为什么要配置为18000-1,80-1我在下面会细细解说。
2.2 计算ARR,PSC和每度的计数值
还是看到这张图
先算出PSC和ARR的乘积
算到这里就完成了第一步,接下来需要确定ARR-1的具体值才能将PSC-1求出,对于ARR-1的值,需要按照当前的情况计算。这里可以使用此方法快速得出ARR-1的值:
然后在加上此函数则可将舵机精度控制为1度,如果有精度为0.1度的需求也可以按上面的方法。
//从450开始每十个计数为一度,到2250
void set_sg90_angle(uint16_t angle)
{
//-90度对应的起始脉冲宽度为0.5ms,及计数值为450
//按照设置每计10个数角度+1
uint16_t count=450+angle*10;
TIM_SetCompare3(TIM3,count);
}
三、电位器驱动
电位器驱动使用ADC模块:
void AD_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //GPIO口时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //adc1时钟配置
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //adc1分频器配置,配置为6分频
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; //配置为模拟输入模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); //配置adc1的规则通道,如果想配置多个可向下复制
ADC_InitTypeDef ADC_InitStructure; //建立adc1的结构体
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE; //是否开启连续模式,否
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //数据左或右对其,右
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; //外部时钟配置,无
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; //独立或交叉模式,独立
ADC_InitStructure.ADC_NbrOfChannel=1; //通道数量,1
ADC_InitStructure.ADC_ScanConvMode=DISABLE; //是否配置多通道模式,否
ADC_Init(ADC1,&ADC_InitStructure);
ADC_Cmd(ADC1,ENABLE); //adc1启动使能
//校准模块
ADC_ResetCalibration(ADC1); //开始复位adc1
while (ADC_GetResetCalibrationStatus(ADC1) == SET); //等待复位结束,跳出循环
ADC_StartCalibration(ADC1); //开始校准adc1
while (ADC_GetCalibrationStatus(ADC1) == SET); //等待adc1校准结束,跳出循环
}
获取当前ADC值:
uint16_t get_adc_data()
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //软件开启转换使能
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET); //adc1(规则和注入)转换结束标志位,0为正在转换,1为转换完成,转换完成跳出while
return ADC_GetConversionValue(ADC1); //获取adc1数据寄存器的12位数据
}
四、最终演示代码
int main()
{
uint16_t ADvalue=0;
uint16_t angle=0;
OLED_Init();
PWM_Init();
AD_Init();
OLED_ShowString(1,1,"angle:");
while(1)
{
ADvalue=get_adc_data(); //获取ADC值
angle=ADvalue/22; // 4095/180=22
OLED_ShowNum(3,1,ADvalue,4);
set_sg90_angle(angle);
}
}
最终实物效果:
stm32电位器控制舵机
五、结语
对于本次的设计,可以提高对PWM和ADC的理解。如有不足,请予指正,感谢!
作者:紫色小薇