This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hi all,
I have a university project. I have code to control servo SG90 by timer. It works, but not accurate. The servo just rotates from 0 to 100 (from the right to the center) , so it is unsuitable for the project. In this code, I want to set 40MHz to synchronize to PWM hardware, which is used to control other servos. Can you help me? This is my code.
Thanks.
#include <stdint.h> #include <stdbool.h> #include "stdlib.h" #include "inc/tm4c123gh6pm.h" #include "inc/hw_memmap.h" #include "inc/hw_uart.h" #include "inc/hw_gpio.h" #include "inc/hw_timer.h" #include "inc/hw_types.h" #include "driverlib/pin_map.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "driverlib/debug.h" #include "driverlib/systick.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/uart.h" #include "driverlib/udma.h" #include "driverlib/gpio.h" #include "driverlib/timer.h" #include <string.h> #define RESOLUTION 10 #define MIN_RANGE 600 #define MAX_RANGE 2400 #define DELAYTIME 300 /* #define FREQ 50 #define PERIOD 40000000/FREQ #define PULSE 40 */ uint32_t pulse, Period; void ServoWrite(int32_t angle) { uint32_t duty; duty = pulse * (( (angle * RESOLUTION) + MIN_RANGE)); TimerMatchSet(TIMER3_BASE, TIMER_A, duty-1); } void delayMS(uint32_t ms) { SysCtlDelay( (SysCtlClockGet()/(3*1000))*ms ) ; } int main(void) { uint32_t dutyCycle; SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); pulse = (SysCtlClockGet() / 1000000 ) ; //the number of pulse per microsecond Period = pulse * 20000; //20ms dutyCycle = pulse * (((90 * RESOLUTION) + MIN_RANGE)); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); GPIOPinConfigure(GPIO_PB2_T3CCP0); GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_2); SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER3); TimerConfigure(TIMER3_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM); TimerControlLevel(TIMER3_BASE, TIMER_A, true); TimerPrescaleSet(TIMER3_BASE, TIMER_A, 12); TimerLoadSet(TIMER3_BASE, TIMER_A, Period - 1); TimerMatchSet(TIMER3_BASE, TIMER_A, dutyCycle -1); TimerEnable(TIMER3_BASE, TIMER_A); uint32_t i=0; while(1){ for(i = 1; i < 110; i++) { ServoWrite(i); delayMS(20); } delayMS(1000); for(i = 110-1; i > 0; i--) { ServoWrite(i); delayMS(20); } } }
Hi Bao,
First, I refer to 40Mhz/50 to simplify obtaining the Period value to load into the timer which you do by obtaining the 1uS Period and multiplying by 20000uS.
Second, do the math. 40Mhz/50 (or the math you have there which is equivalent) is equal to 800000. You are using a 32/16 bits general purpose timer for PWM generation. So the maximum Load value is only 65535! You have to either use the prescaler or use a wide timer which is 64/32 bits.
You do use a prescaler with value 12 while also using the value 800000. Although I see why you do that, you should not load 800000 into the timer. You should find a better value to approximate the required 20mS for the servo.
Also remember that using a prescaler lowers accuracy.
Third, I am not quite sure if at 40Mhz there's a problem, I think it's just over 40Mhz system clock, but SysCtlClockGet() has a bug in which it returns the wrong clock, could you check the value it returns?
With all that math I give you more math.
You have a min range of 600uS (seems low, normally its 1mS). You have there trying to get the value 110 into your function.
So (110*10)+600 it gives you 1700, 1,7mS. That's about half the servo position.
But notice, what is the "pulse" value? It's 40. So 40*1700 = 68000. It's over the max value of the timer which is 65535. You should be operating now also with the prescaler match value! See the problem?
If you reverse the equation 65535 = 40 * (angle *10) + 600 it gives you a maximum value for angle of about 103!
See the problem? Just that little detail.
´
Now try to understand what I wrote and see which is the best solution to solve your problem. I hope I was of help :).
Hi Luis,
I come back :)
The new code works. However, there are some issues.
I want to control servos independently by PWM hardware, but some of them run at the same time. Additionally, their position changes during the Debug process instead of being fixed. Can you show me solutions? Thanks again.
Luis Afonso said:With all that math I give you more math.
Plot thickens - love it when posters (slowly/gradually) lengthen their requests...
Appears that (still more) math lands - the Luis "help desk..." (appears vendor's "usual" is vacationing...)
And (now) you're told of multiple servos - and their joint control...