NVIDIA / jetson-gpio

A Python library that enables the use of Jetson's GPIOs
MIT License
917 stars 259 forks source link

Jetson Orin Nano PWM only works on Pin 33 #105

Open mattwilliamson opened 8 months ago

mattwilliamson commented 8 months ago

Hello,

Pin 33 works with PWM. Pin 32 and pin 15 do not. It's just either on or off. I'm using a L293 motor controller and if I switch the pins for left and right motors, the other motor exhibits the same behavior.

Jetson Orin Nano
Jetpack 5.1.2 L4T 35.4.1
Jetson.GPIO.VERSION '2.1.6'

>>> GPIO.JETSON_INFO
{'P1_REVISION': 1, 'RAM': '32768M, 65536M', 'REVISION': 'Unknown', 'TYPE': 'JETSON_ORIN_NANO', 'MANUFACTURER': 'NVIDIA', 'PROCESSOR': 'A78AE'}

Here's my sample code:

import Jetson.GPIO as GPIO
import time

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)

# Enable, forward, backward
lpin = [32, 38, 37]
rpin = [33, 35, 36]
hz = 50

GPIO.setup(lpin[0], GPIO.OUT)
GPIO.setup(rpin[0], GPIO.OUT)
GPIO.setup(lpin[1],GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(rpin[1],GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(lpin[2], GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(rpin[2], GPIO.OUT, initial=GPIO.LOW)

lpwm = GPIO.PWM(lpin[0], hz)
rpwm = GPIO.PWM(rpin[0], hz)

lpwm.start(0)
rpwm.start(0)

#

def forward(left_speed=100, right_speed=100):
    lpwm.ChangeDutyCycle(left_speed)
    rpwm.ChangeDutyCycle(right_speed)
    GPIO.output(lpin[1],GPIO.HIGH)
    GPIO.output(rpin[1],GPIO.HIGH)
    GPIO.output(lpin[2],GPIO.LOW)
    GPIO.output(rpin[2],GPIO.LOW)

for i in range(0, 100, 1):
    print(i)
    forward(i, 0)
    time.sleep(.1)

Some more info for you:

At 50% duty cycle on pin 32 and 33:

matt@deepdrive:~$ sudo cat /sys/kernel/debug/pwm
platform/39c0000.tachometer, 1 PWM device
 pwm-0   ((null)              ): period: 0 ns duty: 0 ns polarity: normal

platform/32e0000.pwm, 1 PWM device
 pwm-0   (sysfs               ): requested enabled period: 20000000 ns duty: 0 ns polarity: normal

platform/32c0000.pwm, 1 PWM device
 pwm-0   (sysfs               ): requested enabled period: 20000000 ns duty: 10000000 ns polarity: normal

platform/32a0000.pwm, 1 PWM device
 pwm-0   (pwm-fan             ): requested enabled period: 45334 ns duty: 14045 ns polarity: normal

platform/3280000.pwm, 1 PWM device
 pwm-0   (sysfs               ): requested enabled period: 20000000 ns duty: 0 ns polarity: normal
mattwilliamson commented 8 months ago

Once python is running with GPIO, this seems to be able to control the duty cycle in another shell:

echo 0 > /sys/class/pwm/pwmchip3/export
echo 20000000 > /sys/class/pwm/pwmchip3/pwm0/period
echo 10000000 > /sys/class/pwm/pwmchip3/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip3/pwm0/enable
anhmiuhv commented 8 months ago

Did you change the pinmux so they are pwm?

mattwilliamson commented 8 months ago

Yep. I don't think the /sys/class/pwm/pwmchip3/pwm0/duty_cycle would have worked without that.

Screenshot 2024-01-17 at 11 09 07 AM
anhmiuhv commented 8 months ago

Does the code run without crashing and the output signal is correct for pin 33 but the output signal is not correct for pin 15 and 32?

anhmiuhv commented 8 months ago

Once python is running with GPIO, this seems to be able to control the duty cycle in another shell:

echo 0 > /sys/class/pwm/pwmchip3/export
echo 20000000 > /sys/class/pwm/pwmchip3/pwm0/period
echo 10000000 > /sys/class/pwm/pwmchip3/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip3/pwm0/enable

Yes actually this is how the underlying python code control GPIO PWM. You can do this to control it manually

mattwilliamson commented 8 months ago

Does the code run without crashing and the output signal is correct for pin 33 but the output signal is not correct for pin 15 and 32?

Yes, that's correct.

mattwilliamson commented 8 months ago

There's no software PWM support, right? I'm just thinking of workarounds until this can get fixed.

anhmiuhv commented 8 months ago

No software PWM support. Maybe you can use external servo driver like this https://www.amazon.com/16-Channel-12-bit-Servo-Driver-Interface/dp/B00EIB0U7A

anhmiuhv commented 8 months ago

Can you show me the output of

sudo busybox devmem 0x02440020 sudo busybox devmem 0x02434080

mattwilliamson commented 8 months ago
$ sudo busybox devmem 0x02440020
0x00000404
$ sudo busybox devmem 0x02434080
0x00000404
anhmiuhv commented 8 months ago

I cannot reproduce this on my hardware. I will let our hardware engineer know to look into this further.

mattwilliamson commented 8 months ago

If you have instructions on how you got yours working, I could double check that I'm doing it correctly.

anhmiuhv commented 8 months ago

I tested PWM using https://github.com/NVIDIA/jetson-gpio/blob/master/samples/test_all_apis.py To run it you need to connect pin 33 - 19 and pin 11 - 13, and set up pin 15,32,33 as PWM . I modify the test https://github.com/NVIDIA/jetson-gpio/blob/af27a7db4adca91f40fb1aac75a730e7f9c5ba4a/samples/test_all_apis.py#L38 so that "33" is replaced with 15 and 32, and reconnect the jumper wire correspondingly to see if PWM work correctly for those pin

mattwilliamson commented 8 months ago

Nice! I will try it in the morning.

mattwilliamson commented 8 months ago

Here is one thing to note:

>>> GPIO.cleanup()
OSError: [Errno 9] Bad file descriptor

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/matt/src/deepdrive/venv/lib/python3.8/site-packages/Jetson/GPIO/gpio.py", line 381, in cleanup
    _cleanup_all()
  File "/home/matt/src/deepdrive/venv/lib/python3.8/site-packages/Jetson/GPIO/gpio.py", line 273, in _cleanup_all
    _cleanup_one(ch_info)
  File "/home/matt/src/deepdrive/venv/lib/python3.8/site-packages/Jetson/GPIO/gpio.py", line 248, in _cleanup_one
    _unexport_pwm(ch_info)
  File "/home/matt/src/deepdrive/venv/lib/python3.8/site-packages/Jetson/GPIO/gpio.py", line 201, in _unexport_pwm
    ch_info.f_duty_cycle.close()
OSError: [Errno 9] Bad file descriptor
mattwilliamson commented 8 months ago

test_all_apis.py passed with pin32.

Am I correct in that to test pin 15, I connect pin 15 to 19 instead of 33 - 19?

If I do that, I see this:

Testing test_warnings_off
Testing test_warnings_on
Testing test_setup_one_board
Testing test_setup_one_bcm
Testing test_setup_one_cvm
Testing test_setup_one_tegra_soc
Testing test_setup_twice
Testing test_setup_one_out_no_init
Testing test_setup_one_out_high
Testing test_setup_one_out_low
Testing test_setup_many_out_no_init
Testing test_setup_many_out_one_init
Testing test_setup_many_out_many_init
Testing test_setup_one_in
Testing test_setup_one_in_pull
/home/matt/src/deepdrive/venv/lib/python3.8/site-packages/Jetson/GPIO/gpio.py:345: UserWarning: Jetson.GPIO ignores setup()'s pull_up_down parameter
  warnings.warn("Jetson.GPIO ignores setup()'s pull_up_down parameter")
Testing test_setup_many_in
Testing test_setup_all
Testing test_cleanup_one
Testing test_cleanup_many
Testing test_cleanup_all
Testing test_input
Testing test_output_one
Testing test_output_many_one_value
Testing test_output_many_many_value
Testing test_out_in_init_high
Testing test_out_in_init_low
Testing test_gpio_function_unexported
Testing test_gpio_function_in
Testing test_gpio_function_out
Testing test_wait_for_edge_timeout
Testing test_wait_for_edge_rising
Testing test_wait_for_edge_falling
Testing test_event_detected_falling
Testing test_event_detected_rising
Testing test_event_detected_both
Testing test_event_callbacks
Testing test_multi_events_detected_diff_edge
Testing test_multi_events_detected_same_edge
Testing test_pwm_multi_duty
Traceback (most recent call last):
  File "test_all_apis.py", line 1084, in <module>
    test()
  File "test_all_apis.py", line 1009, in test_pwm_multi_duty
    assert min_ct <= count <= max_ct
AssertionError
mattwilliamson commented 8 months ago

Oh it seems that I figured it out. It's quite interesting. Order matters. If I do all the pin setup one at a time it is working.

This will NOT WORK:

GPIO.setup(lpin[0], GPIO.OUT)
GPIO.setup(rpin[0], GPIO.OUT)

GPIO.setup(lpin[1],GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(rpin[1],GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(lpin[2], GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(rpin[2], GPIO.OUT, initial=GPIO.LOW)

lpwm = GPIO.PWM(lpin[0], hz)
rpwm = GPIO.PWM(rpin[0], hz)

lpwm.start(0)
rpwm.start(0)

This WILL work:

GPIO.setup(lpin[1],GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(rpin[1],GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(lpin[2], GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(rpin[2], GPIO.OUT, initial=GPIO.LOW)

# Setup this pin and pwm first
GPIO.setup(lpin[0], GPIO.OUT)
lpwm = GPIO.PWM(lpin[0], hz)
lpwm.start(0)

# then this one
GPIO.setup(rpin[0], GPIO.OUT)
rpwm = GPIO.PWM(rpin[0], hz)
rpwm.start(0)

I'm not sure if it's a bug or if it's a hardware limitation that should be documented.