Hello!
I am trying to write a program for controlling a servo motor. I got recommended a code for making a PWM signal by someone else from devzone, so I am using a modified version of that. I am getting it to work, but only partly. I am having some problems figuring out the duty cylcles and how to position the servo properly.
I know the servo has a operating frequency of between 50Hz, and that it has a pulse width range from 1000 to 2000 microseconds. To set the timer reload value I calculated that 320000 with clock frequency of 16MHz gives a PWM frequency of 50Hz. But when I set the position, I found from trial and error that between 10000 and 40000 gives me about 180 degrees of operation. This isn't quiet what I was expecting, how can I find the proper values so that I know how to accurately control the servo?
Thanks!
#include <zephyr.h> #include <sys/printk.h> #include <math.h> #define PWM_1 3 //Custom pin for servo control // Peripheral channel assignments #define PWM0_GPIOTE_CH 0 #define PWM0_PPI_CH_A 0 #define PWM0_PPI_CH_B 1 #define PWM0_TIMER_CC_NUM 0 #define PWM1_GPIOTE_CH 1 #define PWM1_PPI_CH_A 2 //#define PWM1_PPI_CH_B 3 #define PWM1_TIMER_CC_NUM 1 #define PWMN_GPIOTE_CH {PWM0_GPIOTE_CH, PWM1_GPIOTE_CH, 2, 3} #define PWMN_PPI_CH_A {PWM0_PPI_CH_A, PWM1_PPI_CH_A, 3, 4} #define PWMN_PPI_CH_B {PWM0_PPI_CH_B, PWM0_PPI_CH_B, 5, 5} #define PWMN_TIMER_CC_NUM {PWM0_TIMER_CC_NUM, PWM1_TIMER_CC_NUM, 2, 3} static uint32_t pwmN_gpiote_ch[] = PWMN_GPIOTE_CH; static uint32_t pwmN_ppi_ch_a[] = PWMN_PPI_CH_A; static uint32_t pwmN_ppi_ch_b[] = PWMN_PPI_CH_B; static uint32_t pwmN_timer_cc_num[] = PWMN_TIMER_CC_NUM; // TIMER3 reload value. The PWM frequency equals '16000000 / TIMER_RELOAD' #define TIMER_RELOAD 320000 // The timer CC register used to reset the timer. Be aware that not all timers in the nRF52 have 6 CC registers. #define TIMER_RELOAD_CC_NUM 5 // This function initializes timer 3 with the following configuration: // 24-bit, base frequency 16 MHz, auto clear on COMPARE5 match (CC5 = TIMER_RELOAD) void timer_init() { NRF_TIMER3->BITMODE = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos; NRF_TIMER3->PRESCALER = 0; NRF_TIMER3->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER_RELOAD_CC_NUM; NRF_TIMER3->MODE = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos; NRF_TIMER3->CC[TIMER_RELOAD_CC_NUM] = TIMER_RELOAD; } // Starts TIMER3 void timer_start() { NRF_TIMER3->TASKS_START = 1; } // This function sets up TIMER3, the PPI and the GPIOTE modules to configure a single PWM channel. // Timer CC num, PPI channel nums and GPIOTE channel num is defined at the top of this file void pwm0_init(uint32_t pinselect) { NRF_GPIOTE->CONFIG[PWM0_GPIOTE_CH] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos | pinselect << GPIOTE_CONFIG_PSEL_Pos | GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos; NRF_PPI->CH[PWM0_PPI_CH_A].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[PWM0_TIMER_CC_NUM]; NRF_PPI->CH[PWM0_PPI_CH_A].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[PWM0_GPIOTE_CH]; NRF_PPI->CH[PWM0_PPI_CH_B].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[TIMER_RELOAD_CC_NUM]; NRF_PPI->CH[PWM0_PPI_CH_B].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[PWM0_GPIOTE_CH]; NRF_PPI->CHENSET = (1 << PWM0_PPI_CH_A) | (1 << PWM0_PPI_CH_B); } // Function for changing the duty cycle on PWM channel 0 void pwm0_set_duty_cycle(uint32_t value) { if(value == 0) { value = 1; } else if(value >= TIMER_RELOAD) { value = TIMER_RELOAD - 1; } NRF_TIMER3->CC[PWM0_TIMER_CC_NUM] = value; } int main(void) { printk("Hello World! %s\n", CONFIG_BOARD); uint32_t counter = 0; // Initialize the timer timer_init(); // Initialize PWM channel 0 pwm0_init(PWM_1); // Start the timer timer_start(); for (;;) { k_sleep(K_MSEC(4)); // Update the duty cycle of PWM channel 0 and increment the counter pwm0_set_duty_cycle(30000); } }