I want understand the code of PWD and I2C class of robot_hat with picarx

The __ init ___ method of picarx.py creat 3 instance of SERVO
each is on canal P0, P1, P2,

servo_pins:list=[‘P0’, ‘P1’, ‘P2’]

 51         self.cam_pan = Servo(servo_pins[0])
 52         self.cam_tilt = Servo(servo_pins[1])
 53         self.dir_servo_pin = Servo(servo_pins[2])

There are no specific features that distinguish them, apart from the channel.

The _init_ method of the SERVO class creates an instance of the PWM class with the following property: period == 4095

The instance of the PWM class calculates the prescaler value, which is equal to 352 for a frequency of 50 Hz and a clock speed of 72 MHz.

The PWM class instance then creates an instance of the I2C class. This opens a communication channel at address 0x14 on bus 1.

The PWM class instance can then write the previously calculated data to a register.

  • The prescaler traces the value 352 (0x15F) to address 0x40
    I2C-write( self, data=[64, 1, 95]==[0x40, 0x1, 0x5f, ]

  • And for the period, the value 4095 (0xFFF) is written to address 0x44
    I2C-write( self, data=[68, 15, 255]==[0x44, 0xf, 0xff, ]So far, so good.

This process is repeated three times:
once for channel P0, once for channel P1 and once for channel P2
OK, that makes sense…

But where I’m getting stuck is that these configuration values are written to exactly the same registers at addresses 0x40 and 0x44.

Everything works perfectly, so I understand why the same data is written for three different servo motors, but I don’t understand why the same write registers are used.

Any ideas, perhaps? Thanks

Hello, thank you for analyzing the code in such detail. Your observation is correct.

P0, P1, and P2 write the Prescaler and Period to the same registers because these three PWM channels share the same hardware timer.

In pwm.py, the timers are assigned as follows:

if channel < 16:
self.timer_index = int(channel / 4)
So the PWM channels are grouped like this:

P0~P3 → Timer 0
P4~P7 → Timer 1
P8~P11 → Timer 2
P12~P15 → Timer 3
The timer_index of P0, P1, and P2 is all 0, so they share Timer 0’s frequency configuration registers:

0x40 → Timer 0’s Prescaler register
0x44 → Timer 0’s Period register
Since all three servos need to use the same 50Hz PWM frequency, writing the same configuration values to these two registers repeatedly is normal. Each time a Servo instance is created, the program re-initializes its corresponding timer, so these settings are written three times. The writes after the first one are redundant, but they do not cause any conflict.

The part that is truly independent for each servo is its own pulse-width register:

P0 → 0x20
P1 → 0x21
P2 → 0x22
In other words, 0x40 and 0x44 set the PWM frequency shared by the three channels, while 0x20, 0x21, and 0x22 set the pulse width of each servo individually, allowing each one to be controlled independently.

Also, the program calculates a Prescaler of 352, but the value actually written to the register is 351, i.e. 0x015F. This is because the hardware register stores Prescaler - 1.

So the current behavior is normal: P0, P1, and P2 share the same timer frequency configuration, but each servo is still controlled through its own independent channel register.

If you have any further questions, feel free to ask!

Thank you for all this information. It’s now clearer to me.

I do have some questions, but I’m going to close this thread first since I’ve gotten the answer to my question.
I’m going to start a new topic about the interactions between instances of the Servo and PWM classes.