How to set time period of pulse in PWM

General discussion on mikroC PRO for PIC.
Post Reply
Author
Message
kumar123
Posts: 68
Joined: 17 Oct 2023 07:32

How to set time period of pulse in PWM

#1 Post by kumar123 » 12 Dec 2023 06:47

Hi,
I am using PIC16f1526 micro-controller and PCA9685 IC to control servo motor. This below code I am using-

Code: Select all

#define Write_address 0x80  // I2C address for PCA9865 with no solder bridges
#define Read_address Write_address+1
#define PCA9685_software_reset 0x06
#define Reset   0x01        // Reset the device
#define MODE1   0x00        // 0x00 location for Mode1 register address
#define MODE2   0x01        // 0x01 location for Mode2 reigster address
#define LED0    0x06        // location for start of LED0 registers
#define ALL_CH_ON_L_reg   0xFA
#define ALL_CH_ON_H_reg   0xFB
#define ALL_CH_OFF_L_reg  0xFC
#define ALL_CH_OFF_H_reg  0xFD
#define HEARTBEAT LATC0_bit // green led on ETT board
sbit SCL_i2c at RC3_bit;
sbit SDA_i2c at RC4_bit;
sbit SCL_direction at TRISC3_bit;
sbit SDA_direction at TRISC4_bit;
////////////////////////////////////////////////////////////////////////////////
// Prototype functions - functions at end
void PCA9685_init();
void PCA9685_send(unsigned int value, unsigned char led);
void PCA9685AllLedOff();
unsigned char PCA9685_read_byte(unsigned char chip_register);
void PCA9685_write_byte(unsigned char chip_register, unsigned char value);
void PCA9685_write_word(unsigned char chip_register, unsigned int word_value);
void PCA9685_soft_reset();
////////////////////////////////////////////////////////////////////////////////
void main(){
    // block variables
    unsigned char              n = 0;   // LED flash
    unsigned int            loop = 0;   // loop counter
    unsigned int             pwm = 0;
    unsigned int                 x=0;
    
     OSCCON = 0x7a;    //0x7A     //122
     SCL_i2c = 0;
     SDA_i2c = 0;
     SCL_direction = 0;
     SDA_direction = 0;
  
    ADCON0 = 1;
   
    I2C1_Init(100000);
    PCA9685_init();

    PCA9685_send((int)4096 * 0.5 ,0); //     (int)4096 * 0.5
         
////////////////////////////////////////////////////////////////////////////////
// Functions
// Init the chip with pwm frequency and MODE2 settings
void PCA9685_init(){
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 ADDRESS
     I2C1_Wr(0b00110001);      // Sleep and change default PWM frequency     00110001
     I2C1_stop();              // Stop
     delay_ms(1);              // Required 50 us delay
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(0xFE);            // PWM frequency PRE_SCALE ADDRESS to set pwm at 100Hz   fe
     I2C1_Wr(6);            // Osc_clk/(4096*update_rate)=25000000/(4096*100)=60=0x3C    //06
     I2C1_stop();              // Stop
     delay_ms(1);              // delay at least 500 us
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 register ADDRESS
     I2C1_Wr(0b10100001);      // Set MODE1          //10100001
     I2C1_stop();              // Stop
     delay_ms(1);              // delay at least 500 us
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Address
     I2C1_Wr(MODE2);           // Mode2 register ADDRESS
     I2C1_Wr(0b00000100);      // Set MODE2 //00000100
     I2C1_stop();              //
}
// Send pulse length[0-4095] to selected LED/SERVO[0-15]
void PCA9685_send(unsigned int value, unsigned char led){
     unsigned char pulse_length;// temp variable for PWM
     I2C1_start();              // Start
     I2C1_Wr(Write_address);    // address of selected pca9685
     I2C1_Wr(LED0 + 4 * led);   // select slected LED ADDRESS
     I2C1_Wr(0x00);             // LED_ON_L    //00
     I2C1_Wr(0x00);             // LED_ON_H    // 00
     pulse_length = value;      // PWM value lo byte
     I2C1_Wr(pulse_length);     // LED_OFF_L
     pulse_length = value>>8;   // pwm 16 bit long, now shift upper 8 to lower 8
     I2C1_Wr(pulse_length);     // LED_OFF_H
     I2C1_stop();               // stop
}
void PCA9685AllLedOff(){
     I2C1_start();              // atart
     I2C1_Wr(Write_address);    // select pca9685
     I2C1_Wr(ALL_CH_OFF_L_reg); // All LEDs Off regiter
     I2C1_Wr(0b00000000);       // low byte
     I2C1_Wr(0b00010000);       // high byte, bit4 set so full_off see page 21
     I2C1_stop();               // Stop
}
// Read a byte and return it's value
unsigned char PCA9685_read_byte(unsigned char chip_register){
    unsigned char temp = 0x00;
    I2C1_Start();
    I2C1_Wr(Write_address);
    I2C1_Wr(chip_register);
    I2C1_Start();
    I2C1_Wr(Read_address);
    temp = I2C1_Rd(0);
    I2C1_Stop();
    return temp;
}
void PCA9685_write_byte(unsigned char chip_register, unsigned char value){
     I2C1_Start();
     I2C1_Wr(Write_address);
     I2C1_Wr(chip_register);
     I2C1_Wr(value);
     I2C1_Stop();
}
// Write 16bits to chip_register, increments automatically from lo to hi byte
void PCA9685_write_word(unsigned char chip_register, unsigned int word_value){
     unsigned char hb = 0x00;
     unsigned char lb = 0x00;
     lb = (word_value & 0x00FF);
     hb = ((word_value & 0xFF00) >> 0x08);
     PCA9685_write_byte(chip_register,lb);
     PCA9685_write_byte((chip_register+1),hb);
}
// Soft re-set
void PCA9685_soft_reset(){
    I2C1_Start();
    I2C1_Wr(0x00);
    I2C1_Wr(PCA9685_software_reset);
    I2C1_Stop();
}
This code is giving controlled duty cycle like 50%, 25%, etc. But I am not able to set the time period of Pulse 1ms. How to do this?
t1.png
t1.png (6.06 KiB) Viewed 493 times
Currently It's giving first one pulse, And now I am expecting second one pulse

Regards,
Kumar

kumar123
Posts: 68
Joined: 17 Oct 2023 07:32

Re: How to set time period of pulse in PWM

#2 Post by kumar123 » 26 Dec 2023 09:50

Hi,
Could you anyone help me for above query

Regards,
Himanshu Kumar

User avatar
IvanJeremic
mikroElektronika team
Posts: 316
Joined: 05 Sep 2022 14:32

Re: How to set time period of pulse in PWM

#3 Post by IvanJeremic » 27 Dec 2023 10:28

Hi,

Sorry for the delay.

You can find in the datasheet how to control the frequency of the PWM.
Untitled.png
Untitled.png (49.33 KiB) Viewed 416 times
We do not have any example where the time period is changed unfortunately.

The fastest that it can go is 1526Hz.

Regards,

Ivan.

kumar123
Posts: 68
Joined: 17 Oct 2023 07:32

Re: How to set time period of pulse in PWM

#4 Post by kumar123 » 24 Jan 2024 07:52

Hi,
According to your suggestion, I tried to calculate the prescale value, but still I am not getting the exact time period of pulse, it is 1.11 time period of pulse. The duty cycle I am getting 50%, that I have got by doing some mathematical calculations.
I don't know how to calculate the prescale value for PIC16f1526 micro controller and I have 12-bit pca9685 IC module and device clock frequency I am using 20Mhz.
This code I am using to control servo motor.

Code: Select all

#define Write_address 0x80  // I2C address for PCA9865 with no solder bridges
#define Read_address Write_address+1
#define PCA9685_software_reset 0x06
#define Reset   0x01        // Reset the device
#define MODE1   0x00        // 0x00 location for Mode1 register address
#define MODE2   0x01        // 0x01 location for Mode2 reigster address
#define LED0    0x06        // location for start of LED0 registers
#define ALL_CH_ON_L_reg   0xFA
#define ALL_CH_ON_H_reg   0xFB
#define ALL_CH_OFF_L_reg  0xFC
#define ALL_CH_OFF_H_reg  0xFD
#define HEARTBEAT LATC0_bit // green led on ETT board
sbit SCL_i2c at RC3_bit;
sbit SDA_i2c at RC4_bit;
sbit SCL_direction at TRISC3_bit;
sbit SDA_direction at TRISC4_bit;
////////////////////////////////////////////////////////////////////////////////
// Prototype functions - functions at end
void PCA9685_init();
void PCA9685_send(unsigned int value, unsigned char led);
void PCA9685AllLedOff();
unsigned char PCA9685_read_byte(unsigned char chip_register);
void PCA9685_write_byte(unsigned char chip_register, unsigned char value);
void PCA9685_write_word(unsigned char chip_register, unsigned int word_value);
void PCA9685_soft_reset();
////////////////////////////////////////////////////////////////////////////////
void main(){
    // block variables
    unsigned char              n = 0;   // LED flash
    unsigned int            loop = 0;   // loop counter
    unsigned int             pwm = 0;
    unsigned int                 x=0;
    
     OSCCON = 0x7a;    //0x7A     //122
     SCL_i2c = 0;
     SDA_i2c = 0;
     SCL_direction = 0;
     SDA_direction = 0;
  
    ADCON0 = 1;
   
    I2C1_Init(100000);
    PCA9685_init();

    PCA9685_send((int)4096 * 0.5 ,0); //     (int)4096 * 0.5
         
////////////////////////////////////////////////////////////////////////////////
// Functions
// Init the chip with pwm frequency and MODE2 settings
void PCA9685_init(){
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 ADDRESS
     I2C1_Wr(0b00110001);      // Sleep and change default PWM frequency     00110001
     I2C1_stop();              // Stop
     delay_ms(1);              // Required 50 us delay
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(0xFE);            // PWM frequency PRE_SCALE ADDRESS to set pwm at 100Hz   fe
     I2C1_Wr(6);            // Osc_clk/(4096*update_rate)=25000000/(4096*100)=60=0x3C    //06
     I2C1_stop();              // Stop
     delay_ms(1);              // delay at least 500 us
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 register ADDRESS
     I2C1_Wr(0b10100001);      // Set MODE1          //10100001
     I2C1_stop();              // Stop
     delay_ms(1);              // delay at least 500 us
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Address
     I2C1_Wr(MODE2);           // Mode2 register ADDRESS
     I2C1_Wr(0b00000100);      // Set MODE2 //00000100
     I2C1_stop();              //
}
// Send pulse length[0-4095] to selected LED/SERVO[0-15]
void PCA9685_send(unsigned int value, unsigned char led){
     unsigned char pulse_length;// temp variable for PWM
     I2C1_start();              // Start
     I2C1_Wr(Write_address);    // address of selected pca9685
     I2C1_Wr(LED0 + 4 * led);   // select slected LED ADDRESS
     I2C1_Wr(0x00);             // LED_ON_L    //00
     I2C1_Wr(0x00);             // LED_ON_H    // 00
     pulse_length = value;      // PWM value lo byte
     I2C1_Wr(pulse_length);     // LED_OFF_L
     pulse_length = value>>8;   // pwm 16 bit long, now shift upper 8 to lower 8
     I2C1_Wr(pulse_length);     // LED_OFF_H
     I2C1_stop();               // stop
}
void PCA9685AllLedOff(){
     I2C1_start();              // atart
     I2C1_Wr(Write_address);    // select pca9685
     I2C1_Wr(ALL_CH_OFF_L_reg); // All LEDs Off regiter
     I2C1_Wr(0b00000000);       // low byte
     I2C1_Wr(0b00010000);       // high byte, bit4 set so full_off see page 21
     I2C1_stop();               // Stop
}
// Read a byte and return it's value
unsigned char PCA9685_read_byte(unsigned char chip_register){
    unsigned char temp = 0x00;
    I2C1_Start();
    I2C1_Wr(Write_address);
    I2C1_Wr(chip_register);
    I2C1_Start();
    I2C1_Wr(Read_address);
    temp = I2C1_Rd(0);
    I2C1_Stop();
    return temp;
}
void PCA9685_write_byte(unsigned char chip_register, unsigned char value){
     I2C1_Start();
     I2C1_Wr(Write_address);
     I2C1_Wr(chip_register);
     I2C1_Wr(value);
     I2C1_Stop();
}
// Write 16bits to chip_register, increments automatically from lo to hi byte
void PCA9685_write_word(unsigned char chip_register, unsigned int word_value){
     unsigned char hb = 0x00;
     unsigned char lb = 0x00;
     lb = (word_value & 0x00FF);
     hb = ((word_value & 0xFF00) >> 0x08);
     PCA9685_write_byte(chip_register,lb);
     PCA9685_write_byte((chip_register+1),hb);
}
// Soft re-set
void PCA9685_soft_reset(){
    I2C1_Start();
    I2C1_Wr(0x00);
    I2C1_Wr(PCA9685_software_reset);
    I2C1_Stop();
}
Regards,
Kumar

zhschip
Posts: 4
Joined: 24 Jan 2024 10:07

Re: How to set time period of pulse in PWM

#5 Post by zhschip » 24 Jan 2024 10:25

It appears that the provided code is a set of functions for controlling the PCA9685 16-channel PWM controller through the I2C interface using a PIC16F1526 microcontroller. The code includes functions for initializing the PCA9685, sending PWM signals to specific channels, turning off all channels, and performing read and write operations.

The code can be broken down into the following sections:

Macro Definitions: Define constants related to I2C addresses, reset code, register addresses, and the heartbeat pin. These are used throughout the code.

I2C Configuration: Configuration of the I2C pins and initialization of the I2C communication interface.

Main Function: Initializes the oscillator, I2C, and the PCA9685. It then sets the PWM signal to the specified channel.

Function Definitions: Function definitions for initializing the PCA9685, sending PWM signals to specific channels, turning off all channels, reading a byte, writing a byte, writing a word, and performing a software reset.

The main function seems to initialize the microcontroller, configure the I2C communication, initialize the PCA9685, and send a PWM signal to a specific channel (in this case, setting the pulse width to 50% of the maximum).

The PCA9685_init function seems to set various registers of the PCA9685 to configure its operation, such as setting the PWM frequency, placing the device in normal mode, setting the output mode, etc.

The PCA9685_send function is likely intended to set the pulse width of specific channels.

Suggestions:

Make sure the connections between the PIC16F1526, PCA9685, and the I2C bus are correct and stable.
Verify the accuracy of the I2C communication by checking the signal using an oscilloscope or logic analyzer, to ensure proper functionality.
Double-check the initialization sequence and configuration of the PCA9685.
Consider adding debugging statements to trace the flow of the code and values being written to the PCA9685 registers, which will help identify any potential issues.
If you're encountering specific issues or have particular questions about this code, please provide additional details so that I can offer more specific assistance.

To set the time period of the pulse to 1ms using the PCA9685 with the provided code, you need to calculate the appropriate values to achieve the desired pulse width based on the frequency setting of the PCA9685. Here are the steps you can take to achieve a 1ms time period:

Calculate the Prescale Value: The PCA9685 uses a prescaler to set the PWM frequency. For a 1ms period (1000Hz), you need to calculate the prescale value.

The formula to calculate the prescale value is:

Plain Text
Copy code
prescale = round((oscillator_frequency / (4096 * desired_frequency))) - 1
In your code I2C1_Wr(0xFE);, the value 0xFE refers to the PRE_SCALE register. You'll want to calculate the appropriate value to write to this register to achieve a 1ms period.

Calculate the ON and OFF Values: Once you have set the prescale value, you can calculate the ON and OFF values for a 1ms pulse width.

The ON register value is when the PWM starts, and the OFF register value is when it stops. Since the PCA9685 is a 12-bit PWM controller, the maximum value is 4095.

For a 1ms pulse, if we assume a 20ms period (typical for servos and most PWM applications), the ON value would be 0, and the OFF value would be 1ms/20ms * 4096 ≈ 205.

Modify PCA9685 Initialization: You need to modify the initialization code for the PCA9685 to set the prescale value, and then when you send a pulse, set the appropriate ON and OFF values to achieve the 1ms time period.

Here's an example of how the code might look to achieve this:

c
Copy code
// Assuming the oscillator frequency is 25MHz
#define oscillator_frequency 25000000

void PCA9685_init_with_1ms_period(){
unsigned int prescale = round((oscillator_frequency / (4096 * 1000))) - 1; // Calculate the prescale for 1ms frequency
// Rest of the initialization code remains as is...
// ... (omit for brevity)

I2C1_start(); // Start
I2C1_Wr(Write_address); // Slave Write_address
I2C1_Wr(0xFE); // PWM frequency PRE_SCALE ADDRESS to set pwm at 1kHz
I2C1_Wr(prescale); // set the calculated prescale
I2C1_stop();

// Rest of the initialization code remains as is...
// ... (omit for brevity)
}

void PCA9685_send_1ms_pulse(unsigned char led){
// Assuming a 20ms period for the servo
unsigned int on_value = 0; // ON value
unsigned int off_value = 205; // OFF value for 1ms pulse width

// Rest of the send pulse code remains as is...
// ... (omit for brevity)
I2C1_start(); // Start
I2C1_Wr(Write_address); // address of selected pca9685
I2C1_Wr(LED0 + 4 * led); // select selected LED ADDRESS
I2C1_Wr(0x00); // LED_ON_L
I2C1_Wr(0x00); // LED_ON_H
I2C1_Wr(on_value & 0xFF); // LED_OFF_L
I2C1_Wr((off_value >> 8) & 0xFF); // LED_OFF_H
I2C1_stop(); // Stop
}
Before using these modified functions, ensure the accuracy of the calculations and their settings by referencing the datasheet and performing testing with the servo. If the servo behaves wrongly, it could be due to compatibility reasons or other electrical issues.
Zhonghaisheng provides original electronic components with low prices, fast delivery,excellent after sales service.with huge selection in stock and ready to ship same day with no minimum orders.New electronic components and parts added every day.

kumar123
Posts: 68
Joined: 17 Oct 2023 07:32

Re: How to set time period of pulse in PWM

#6 Post by kumar123 » 25 Jan 2024 07:56

Hi zhschp,

I have tried according to your suggestion still it is not generating any pulses.
The pres_scale value I have calculated as:
(20000000/(2096 * 1000)) - 1 = 4

I am using PIC16F1527 microcontroller, PCA9685 IC, MCU clock frequency is 20Mhz, and duty cycle is 50%.
The code I I have:

Code: Select all

#define Write_address 0x80  // I2C address for PCA9865 with no solder bridges
#define Read_address Write_address+1
#define PCA9685_software_reset 0x06
#define Reset   0x01        // Reset the device
#define MODE1   0x00        // 0x00 location for Mode1 register address
#define MODE2   0x01        // 0x01 location for Mode2 reigster address
#define LED0    0x06        // location for start of LED0 registers
#define ALL_CH_ON_L_reg   0xFA
#define ALL_CH_ON_H_reg   0xFB
#define ALL_CH_OFF_L_reg  0xFC
#define ALL_CH_OFF_H_reg  0xFD
#define HEARTBEAT LATC0_bit // green led on ETT board
sbit SCL_i2c at RC3_bit;
sbit SDA_i2c at RC4_bit;
sbit SCL_direction at TRISC3_bit;
sbit SDA_direction at TRISC4_bit;
void main(){
    // block variables
    unsigned char              n = 0;   // LED flash
    unsigned int            loop = 0;   // loop counter
    unsigned int             pwm = 0;
    unsigned int                 x=0;
    
     OSCCON = 0x7a;    //0x7A     //122
     SCL_i2c = 0;
     SDA_i2c = 0;
     SCL_direction = 0;
     SDA_direction = 0;
  
    ADCON0 = 1;
   
    I2C1_Init(100000);
    PCA9685_init();

    PCA9685_send((int)4096 * 0.5 ,0); //     (int)4096 * 0.5
    PCA9685_send_1ms_pulse(0);
    
    }
    void PCA9685_init(){
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 ADDRESS
     I2C1_Wr(0b00110001);      // Sleep and change default PWM frequency     00110001
     I2C1_stop();              // Stop
     
     delay_ms(1);              // Required 50 us delay
     
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(0xFE);            // PWM frequency PRE_SCALE ADDRESS to set pwm at 100Hz   fe
     I2C1_Wr(0x05);            // Osc_clk/(4096*update_rate)=25000000/(4096*100)=60=0x3C    //06
     I2C1_stop();              // Stop
     
     delay_ms(1);              // delay at least 500 us
     
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Write_address
     I2C1_Wr(MODE1);           // Mode 1 register ADDRESS
     I2C1_Wr(0b10100001);      // Set MODE1          //10100001
     I2C1_stop();              // Stop
     
     delay_ms(1);              // delay at least 500 us
     
     I2C1_start();             // Start
     I2C1_Wr(Write_address);   // Slave Address
     I2C1_Wr(MODE2);           // Mode2 register ADDRESS
     I2C1_Wr(0b00000100);      // Set MODE2 //00000100
     I2C1_stop();              //
}
// Send pulse length[0-4095] to selected LED/SERVO[0-15]
void PCA9685_send(unsigned int value, unsigned char led){
     unsigned char pulse_length;// temp variable for PWM
     I2C1_start();              // Start
     I2C1_Wr(Write_address);    // address of selected pca9685
     I2C1_Wr(LED0 + 4 * led);   // select slected LED ADDRESS
     I2C1_Wr(0x00);             // LED_ON_L    //00
     I2C1_Wr(0x00);             // LED_ON_H    // 00
     pulse_length = value;      // PWM value lo byte
     I2C1_Wr(pulse_length);     // LED_OFF_L
     pulse_length = value>>8;   // pwm 16 bit long, now shift upper 8 to lower 8
     I2C1_Wr(pulse_length);     // LED_OFF_H
     I2C1_stop();               // stop
}
void PCA9685_send_1ms_pulse(unsigned char led){
// Assuming a 20ms period for the servo
unsigned int on_value = 0; // ON value
unsigned int off_value = 205; // OFF value for 1ms pulse width


I2C1_start(); // Start
I2C1_Wr(Write_address); // address of selected pca9685
I2C1_Wr(LED0 + 4 * led); // select selected LED ADDRESS
I2C1_Wr(0x00); // LED_ON_L
I2C1_Wr(0x00); // LED_ON_H
I2C1_Wr(on_value & 0xFF); // LED_OFF_L
I2C1_Wr((off_value >> 8) & 0xFF); // LED_OFF_H
I2C1_stop(); // Stop
}
Regards,
Kumar

kumar123
Posts: 68
Joined: 17 Oct 2023 07:32

Re: How to set time period of pulse in PWM

#7 Post by kumar123 » 09 Feb 2024 08:29

Hi zhschp,
I am not able to calculate the 'ON' and 'OFF' value for the code.
I also don't know about the osc_clock and update rate.
from where I will took these two things.
you can take reference from previous(above) post.

Regards,
Kumar

Post Reply

Return to “mikroC PRO for PIC General”