Jetson/PWM

From eLinux.org
Jump to: navigation, search

PWM

Pulse Width Modulation (PWM) is often used to control the speed of motors and servo motors or even LEDs, since PWM uses fast digital pulses instead of a continuous analog output voltage. Jetson TK1 offers 2 options for PWM output:

  1. PWM on an external microcontroller such as an Arduino board. This is by far the easiest & best option.
  2. PWM directly in Tegra. This is more tricky and might not give as stable a signal timing.

Note that motors usually require a separate power supply than what Jetson provides. Some low-power motors might be OK with the power available in Jetson power pins or from a USB port but most motors need more power such as directly from the battery or power supply instead of getting power from Jetson or from the Arduino.


PWM on an external microcontroller

The easiest solution for PWM by far is to: connect to Arduino from Jetson TK1, so that your main program running on Jetson decides what the motors should do, and it sends that info to an Arduino that does the actual PWM control. To control motors or servo motors, you can either attach a servo motor shield onto a regular Arduino, or use an Arduino clone that includes its own servo motor controller electronics so it didn't need a separate motor controller shield. But if you use a regular Arduino then the easiest solution would be to add a servo motor shield to a regular Arduino. Because low-level real-time operations like PWM need consistent "hard-realtime" timing that a microcontroller is much more suited to than a big CPU / Application Processor like Tegra.

To send the info to an Arduino or any microcontroller from Jetson TK1, follow the GPIO info about 5V GPIO outputs if you just need 1-way communication, or use a serial UART connection (or even I2C) from Jetson TK1 to the Arduino if you want 2-way communication.

PWM directly in Tegra

You can control PWM output via sysfs at runtime, similar to how you can use GPIO using sysfs. You’ll need to make sure that the pinmux is set up so that PWM is routed to the pin, and that the GPIO for that pin is not claimed, so that the pinmux function is not overridden. Again, you’ll need the following in .config:

CONFIG_PWM_SYSFS=y

Then:

cd /sys/devices/soc0/7000a000.pwm/pwm/pwmchip0

Note: In the L4T kernel, the “soc0” directory component might not be there, and the PWM controller might be named something different like “pwm.0” or “pwm.1”. You might want to just run:

cd /sys
find . –type d –name "pwmchip*"

Note: You might need to replace “0” in the commands below with a different PWM channel ID:

echo 0 > export
cd pwm0
echo 1000000 > period
echo  500000 > duty_cycle   # or whatever number 0..period you want; you can write to this file later to change the duty cycle
echo 1 > enable

and when you’re done:

cd ..
echo 0 > unexport


PWM in L4T rel21

PWM has some issues in L4T r21, requiring some additional configuration changes:

  • pwm0 (on pin 49 of J3A2) is disabled by default in rel21.
  • pwm1 (on pin 50 of J3A1) is enabled by default in rel21 but is being used for the eDP display's backlight.
  • pwm2 (on pin 55 of J3A2) is disabled by default in rel21.
  • pwm3 (on pin 58 of J3A2) is disabled by default in rel21.
To use PWM0, PWM2 and PWM3 in rel21

Apply the following patch:

$ git diff

diff --git a/arch/arm/boot/dts/tegra124-platforms/tegra124-jetson_tk1-gpio-pm375-0000-c00-00.dtsi b/arch/arm/boot/dts/tegra124-platforms/tegra124-jetson_tk1-gpio-pm375-0000-c00-00.dtsi
index d8aa450..555522e 100644
--- a/arch/arm/boot/dts/tegra124-platforms/tegra124-jetson_tk1-gpio-pm375-0000-c00-00.dtsi
+++ b/arch/arm/boot/dts/tegra124-platforms/tegra124-jetson_tk1-gpio-pm375-0000-c00-00.dtsi
@@ -36,10 +36,7 @@
TEGRA_GPIO(U, 0)
TEGRA_GPIO(U, 1)
TEGRA_GPIO(U, 2)
- TEGRA_GPIO(U, 3)
TEGRA_GPIO(U, 4)
- TEGRA_GPIO(U, 5)
- TEGRA_GPIO(U, 6)
TEGRA_GPIO(N, 7)
>;
gpio-output-low = <

diff --git a/arch/arm/boot/dts/tegra124-platforms/tegra124-jetson_tk1-pinmux-pm375-0000-c00-00.dtsi b/arch/arm/boot/dts/tegra124-platforms/tegra124-jetson_tk1-pinmux-pm375-0000-c00-00.dtsi
index 520937d..2f44479 100644
--- a/arch/arm/boot/dts/tegra124-platforms/tegra124-jetson_tk1-pinmux-pm375-0000-c00-00.dtsi
+++ b/arch/arm/boot/dts/tegra124-platforms/tegra124-jetson_tk1-pinmux-pm375-0000-c00-00.dtsi
@@ -1015,10 +1015,10 @@

pu3 {
nvidia,pins = "pu3";
- nvidia,function = "gmi";
+ nvidia,function = "pwm0";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
- nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+ nvidia,enable-input = <TEGRA_PIN_DISABLE>;
};

pu4 {
@@ -1031,18 +1031,18 @@

pu5 {
nvidia,pins = "pu5";
- nvidia,function = "gmi";
+ nvidia,function = "pwm2";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
- nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+ nvidia,enable-input = <TEGRA_PIN_DISABLE>;
};

pu6 {
nvidia,pins = "pu6";
- nvidia,function = "rsvd3";
+ nvidia,function = "pwm3";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
- nvidia,enable-input = <TEGRA_PIN_ENABLE>;
+ nvidia,enable-input = <TEGRA_PIN_DISABLE>;
};

hdmi_int_pn7 {
To use PWM1 in rel21:

PWM1 usage is kind of hard-coded in present code and thus as such user can not use this pwm on pin 50 of J3A1 for any other purpose. But NVIDIA will soon integrate the following patch into the rel21 branch of L4T to free up pwm1 when the edp panel is not connected:

$ git diff

diff --git a/arch/arm/mach-tegra/board-ardbeg-panel.c b/arch/arm/mach-tegra/board-ardbeg-panel.c
index c7da1e3..f954956 100644
--- a/arch/arm/mach-tegra/board-ardbeg-panel.c
+++ b/arch/arm/mach-tegra/board-ardbeg-panel.c
@@ -765,7 +765,6 @@ static struct tegra_panel *ardbeg_panel_configure(struct board_info *board_out,
tegra_io_dpd_enable(&dsid_io);
break;
default:
- panel = &dsi_p_wuxga_10_1;
tegra_io_dpd_enable(&dsic_io);
tegra_io_dpd_enable(&dsid_io);
break;
@@ -775,7 +774,7 @@ static struct tegra_panel *ardbeg_panel_configure(struct board_info *board_out,
return panel;
}

-static void ardbeg_panel_select(void)
+static struct tegra_panel *ardbeg_panel_select(void)
{
struct tegra_panel *panel = NULL;
struct board_info board;
@@ -828,6 +827,8 @@ static void ardbeg_panel_select(void)
panel->register_i2c_bridge();
}

+ return panel;
+
}
#endif

@@ -837,7 +838,7 @@ int __init ardbeg_panel_init(void)
struct resource __maybe_unused *res;
struct platform_device *phost1x = NULL;
struct board_info board_info;
-
+ struct tegra_panel *panel = NULL;
struct device_node *dc1_node = NULL;
struct device_node *dc2_node = NULL;
#ifdef CONFIG_NVMAP_USE_CMA_FOR_CARVEOUT
@@ -848,7 +849,7 @@ int __init ardbeg_panel_init(void)
find_dc_node(&dc1_node, &dc2_node);

#ifndef CONFIG_TEGRA_HDMI_PRIMARY
- ardbeg_panel_select();
+ panel = ardbeg_panel_select();
#endif

#ifdef CONFIG_TEGRA_NVMAP
@@ -943,8 +944,8 @@ int __init ardbeg_panel_init(void)
tegra_fb2_start, tegra_fb2_size);

#ifndef CONFIG_TEGRA_HDMI_PRIMARY
- if (!of_have_populated_dt() || !dc1_node ||
- !of_device_is_available(dc1_node)) {
+ if (panel && (!of_have_populated_dt() || !dc1_node ||
+ !of_device_is_available(dc1_node))) {
ardbeg_disp1_device.dev.parent = &phost1x->dev;
err = platform_device_register(&ardbeg_disp1_device);
if (err) {

After applying that patch, PWM1 should now be working.

You can test PWM1 by running the following commands (with root permissions):

echo 57 > /sys/class/gpio/export
echo 57 > /sys/class/gpio/unexport

echo 1 > /sys/class/pwm/pwmchip0/export
echo <period in ns> > /sys/class/pwm/pwmchip0/pwm1/period
echo <period in ns> > /sys/class/pwm/pwmchip0/pwm1/duty_cycle
echo 1 > echo <period in ns> > /sys/class/pwm/pwmchip0/pwm1/enable