赞
踩
这节的内容将为大家介绍用stm32单片机做一个舵机控制器,通过旋转电位器,来控制舵机的输出角度。
在讲源码之前我们先复习一下舵机的控制原理,在频率50hz下,给一个0.5ms-2.5ms脉宽的占空比,就可以对舵机进行一个45°-180°的转动。
通过单片机我们该如何实现呢?
1、配置ADC模块,对电位器进行模拟量采集,将采集到的数据变成pwm需要输出的占空比。
2、配置PWM模块,产生一个频率50Hz,脉宽在1ms-2.0ms的方波。
1ms-2.0ms对应的角度为45°-135°。
3、范围转换函数。电位器采集到的数据范围为0-4096,而我们的PWM捕获函数需要的值的范围为1000-2000。因此,需要将0-4096的取值范围转换为1000-2000。
下面我们针对每一部分的函数进行列举说明,ADC和PWM都是摘自原子stm32 例程的,也可以直接从例程中摘取。
ADC.c, adc部分基础配置。
- #include "adc.h"
- #include "delay.h"
-
- //³õʼ»¯ADC
- //ÕâÀïÎÒÃǽöÒÔ¹æÔòͨµÀΪÀý
- //ÎÒÃÇĬÈϽ«¿ªÆôͨµÀ0~3
- void Adc_Init(void)
- {
- ADC_InitTypeDef ADC_InitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //ʹÄÜADC1ͨµÀʱÖÓ
-
-
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ÉèÖÃADC·ÖƵÒò×Ó6 72M/6=12,ADC×î´óʱ¼ä²»Äܳ¬¹ý14M
-
- //PA1 ×÷ΪģÄâͨµÀÊäÈëÒý½Å
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //Ä£ÄâÊäÈëÒý½Å
- GPIO_Init(GPIOA, &GPIO_InitStructure);
-
- ADC_DeInit(ADC1); //¸´Î»ADC1,½«ÍâÉè ADC1 µÄÈ«²¿¼Ä´æÆ÷ÖØÉèΪȱʡֵ ADC1 ADC2 ADC3
-
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC¹¤×÷ģʽ:ADC1ºÍADC2¹¤×÷ÔÚ¶ÀÁ¢Ä£Ê½
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //Ä£Êýת»»¹¤×÷ÔÚµ¥Í¨µÀģʽ
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //Ä£Êýת»»¹¤×÷ÔÚµ¥´Îת»»Ä£Ê½
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //ת»»ÓÉÈí¼þ¶ø²»ÊÇÍⲿ´¥·¢Æô¶¯
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADCÊý¾ÝÓÒ¶ÔÆë
- ADC_InitStructure.ADC_NbrOfChannel = 1; //˳Ðò½øÐйæÔòת»»µÄADCͨµÀµÄÊýÄ¿
- ADC_Init(ADC1, &ADC_InitStructure); //¸ù¾ÝADC_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉèADCxµÄ¼Ä´æÆ÷
-
-
- ADC_Cmd(ADC1, ENABLE); //ʹÄÜÖ¸¶¨µÄADC1
-
- ADC_ResetCalibration(ADC1); //ʹÄܸ´Î»Ð£×¼
-
- while(ADC_GetResetCalibrationStatus(ADC1)); //µÈ´ý¸´Î»Ð£×¼½áÊø
-
- ADC_StartCalibration(ADC1); //¿ªÆôADУ׼
-
- while(ADC_GetCalibrationStatus(ADC1)); //µÈ´ýУ׼½áÊø
-
- // ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ʹÄÜÖ¸¶¨µÄADC1µÄÈí¼þת»»Æô¶¯¹¦ÄÜ
-
- }
- //»ñµÃADCÖµ
- //ch:ͨµÀÖµ 0~3
- u16 Get_Adc(u8 ch)
- {
- //ÉèÖÃÖ¸¶¨ADCµÄ¹æÔò×éͨµÀ£¬Ò»¸öÐòÁУ¬²ÉÑùʱ¼ä PB1¶ÔÓ¦ADC9
- ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADCͨµÀ,²ÉÑùʱ¼äΪ239.5ÖÜÆÚ
-
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ʹÄÜÖ¸¶¨µÄADC1µÄÈí¼þת»»Æô¶¯¹¦ÄÜ
-
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//µÈ´ýת»»½áÊø
-
- return ADC_GetConversionValue(ADC1); //·µ»Ø×î½üÒ»´ÎADC1¹æÔò×éµÄת»»½á¹û
- }
-
- u16 Get_Adc_Average(u8 ch,u8 times)
- {
- u32 temp_val=0;
- u8 t;
- for(t=0;t<times;t++)
- {
- temp_val+=Get_Adc(ch);
- delay_ms(5);
- }
- return temp_val/times;
- }
PWM.c,PWM部分的基础配置。
- #include "pwm.h"
-
-
-
- //PWMÊä³ö³õʼ»¯
- //arr£º×Ô¶¯ÖØ×°Öµ
- //psc£ºÊ±ÖÓÔ¤·ÖƵÊý
- void TIM1_PWM_Init(u16 arr,u16 psc)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- TIM_OCInitTypeDef TIM_OCInitStructure;
-
- 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);
-
-
- 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
-
-
- }
范围转换函数,个人觉得这个函数是整个工程的重点,希望大家能记住这个函数,这个函数是摘自arduino中的函数,记住便可熟练使用。
- float map(float value,float fromLow,float fromHigh,float toLow,float toHigh)
- {
- return ((value-fromLow)*(toHigh-toLow)/(fromHigh-fromLow)+toLow);
- }
主函数 main.c
-
- int main(void)
- {
- u16 pwmval_adc=0;
-
- u16 pwmval_to_range;
-
-
- //u8 adc_value;
- delay_init(); //ÑÓʱº¯Êý³õʼ»¯
- TIM1_PWM_Init(1999,719); //Fre_PWM = 72000/(719+1)/(1999+1)=50hz
- uart_init(9600);
- Adc_Init();
-
- printf("this is a test");
- while(1)
- {
- // printf("this is a test");
- delay_ms(10);
- pwmval_adc = Get_Adc_Average(ADC_Channel_1,10); //adcx Ϊ»ñÈ¡µ½ ADCµÄÖµ
- //printf("adc_value= %d \n\r",pwmval_adc); //´òÓ¡³öADC ²É¼¯µ½µÄµçλÆ÷Öµ 4090
-
- pwmval_to_range = (int)map(pwmval_adc,0,4092,1000,2000);
- delay_ms(100);
- printf("range =%d\t\r\n",pwmval_to_range); //Êä³ö ÔÚ1000-2000·¶Î§ÄÚתÍ귶ΧµÄADC Öµ
-
- TIM_SetCompare1(TIM1,pwmval_to_range);
-
- }
- }
程序下载进去,我们就可以看到舵机按照45°-135°进行往复运动。
欢迎大家关注微信公众号:广乙电子(dlrcclub)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。