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);
}
}
