BeagleBone Black PWM on QNX OS

I would like to have a PWM output signal from BeagleBone Black. I have to use QNX operating System (due to Real-Time Constraints) on my BeagleBone.
Do you have any suggestions how to do that?
There is no out of the box QNX bsp to do that.

Define" “Real-Time constrants”

The beagelbone’s have what is known as a PRU, or Programmable Real-time Unit. These can operate, in “real-time” along side whatever OS is on the beaglebone. Which is one reason among a few to pick this board over another similar board. I am not sure if the PWM’s are accessible to the PRU’s ( there are two ) in a single cycle, but they should be accessible to the PRU regardless.

There are also at least machinekit https://github.com/machinekit/machinekit and xenomai http://xenomai.org/ images / instructions offered at http://elinux.org/Beagleboard:BeagleBoneBlack_Debian#Debian_Image_Testing_Snapshots and http://elinux.org/Beagleboard:BeagleBoneBlack_Debian#3.8.x_BeagleBone.2FBeagleBone_Black_FULL_Cape_Support_.2B_Xenomai

So, there are other options, provided one solution or more fits your needs. I’ve read some posts over the last couple years concerning QNX, but . . . honestly have no interest in it as it is a commercial product. Where the Beaglebone are open source hardware, and software . . .

If you want to use QNX, you'll have to perform all the hardware setup
and configuration yourself. It's reasonably straight-forward to
interface to the PWM registers (see the TRM for details), but you'll
also need to setup the clock multiplexing and resets before anything
will work at all. The Linux kernel code is a great reference for
getting the clocks/resets configured properly, or you can refer to the
TI bare-metal example applications.

...or see if the QNX folks will make a proper AM335x BSP for you. :slight_smile:

http://community.qnx.com/sf/wiki/do/viewPage/projects.bsp/wiki/TiAm335Beaglebone

Sorry , missing the key driver of pwm .
As I remember , QNX dont have the pwm as a architecture driver .

maybe from other bsp source as a reference ?

Here is some code from a Beagleboard (not Beaglebone) the register bases and pin configs will be different, but it should give you an idea of how to control a PWM using QNX. It is not a driver in the QNX sense, but a low-level approach.

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <time.h>
#include <hw/inout.h>

#define PG_SZ 0x1000
#define PAD_CONF_BASE 0x48002000
#define PAD_CTRL_G130_131 0x158 /* 131U 130L /
#define PAD_CTRL_G132_133 0x15c /
133U 132L /
#define PAD_CTRL_G134_135 0x160 /
135U 134L /
#define PAD_CTRL_G136_137 0x164 /
137U 136L /
#define PAD_CTRL_G138_139 0x168 /
139U 138L /
#define PWM_PAD_9 0x174 /
lwr /
#define PWM_PAD_10 0x174 /
upr /
#define PWM_PAD_11 0x178 /
lwr */

#define PAD_MODE1 1
#define PAD_MODE2 2
#define PAD_MODE4 4
#define PAD_PU_EN (1<<3)
#define PAD_PU_UP 16

#define PAD_INPUTENABLE (1<<8)

#define GPIO5_BASE 0x49056000
#define GPIO_DATAIN 0x038
#define GPIO_OE 0x034
#define GPIO_CLEARDATAOUT 0x90
#define GPIO_SETDATAOUT 0x94

#define PER_CLK_BASE 0x48005000
#define GPIO_CLK_EN_GPIO5 (1<<16)
#define ICLK_OFFSET 0x10
#define EN_GPT9 (1<<10)
#define GPT9_CLKSEL_OFFSET 0x40
#define CLKSEL_GPT9 (1<<7)
#define CM_CLK_BASE 0x48004000
#define FCLK_ENABLE_OFFSET 0xA00
#define ICLK_ENABLE_OFFSET 0xA10
#define GPT10_CLKSEL_OFFSET 0xa40
#define EN_GPT10 (1<<11)
#define CLKSEL_GPT10 (1<<6)
#define EN_GPT11 (1<<12)
#define CLKSEL_GPT11 (1<<7)

#define TIMER_9_BASE 0x49040000
#define TIMER_10_BASE 0x48086000
#define TIMER_11_BASE 0x48088000
#define TIMER_TCLR 0x24
#define TIMER_START 1
#define TIMER_AUTORELOAD 2
#define TIMER_CE (1<<6)
#define TIMER_SCPWM (1<<7)
#define TIMER_PT (1<<12)
#define TIMER_TRG (2<<10)
#define TIMER_TCCR 0x28
#define TIMER_TLDR 0x2c
#define TIMER_TMAR 0x38
#define SERVO_TLDR 0xfffc0857UL
#define SYS_CLK 13000000UL
#define SERVO_STOP 0xfffc5483UL
#define SERVO_FWD (SERVO_TLDR + SYS_CLK0.0018)
#define SERVO_REV (SERVO_TLDR + SYS_CLK
0.0008)
#define LEFT_SERVO_TRIM -488

#define LEFT_MOTOR 0
#define RIGHT_MOTOR 1
#define DUMP_MOTOR 2

#define MOTOR_REV -1
#define MOTOR_STOP 0
#define MOTOR_FWD 1

/* GPIO’s are all mode 4
PIN Function PAD_CONF
4 GPT9_PWMEVT (left servo PWM) 0x48002174 L
10 GPT10_PWMEVT (right servo PWM) 0x48002174 U
6 GPT11_PWMEVT (dump servo PWM) 0x48002178 L
21 GPIO_130 (bucket sensor 1) 0x48002158 L
19 GPIO_131 (bucket sensor 2) 0x48002158 U
17 GPIO_132 (sensor 0) 0x4800215C L
15 GPIO_133 (sensor 1) 0x4800215C U
13 GPIO_134 (sensor 2) 0x48002160 L
11 GPIO_135 (start switch) 0x48002160 U
9 GPIO_136 (program switch 1) 0x48002164 L
7 GPIO_137 (program switch 2) 0x48002164 U
5 GPIO_138 (boot LED) 0x48002168 L
*/

/* GPIO5 starts at 128 */
#define BUCKET_SENSOR_1 2
#define BUCKET_SENSOR_2 3
#define SENSOR_GPIO(x) (x+4)
#define START_SWITCH 7
#define PROGRAM_SWITCH_1 8
#define PROGRAM_SWITCH_2 9

static uintptr_t gpio_ptr;
static uintptr_t left_servo_pwm;
static uintptr_t right_servo_pwm;
static uintptr_t dump_servo_pwm;

void init()
{
uintptr_t padconf_ptr, clk_ptr;
uint32_t tmp32;

// turn on clks to GPIO5 and PWM9
clk_ptr = mmap_device_io(PG_SZ, PER_CLK_BASE);
tmp32 = in32(clk_ptr);
out32(clk_ptr, tmp32 | GPIO_CLK_EN_GPIO5 | EN_GPT9); // fclk
tmp32 = in32(clk_ptr + ICLK_OFFSET);
out32(clk_ptr + ICLK_OFFSET, tmp32 | GPIO_CLK_EN_GPIO5 | EN_GPT9); // iclk
tmp32 = in32(clk_ptr + GPT9_CLKSEL_OFFSET);
tmp32 |= CLKSEL_GPT9;
out32(clk_ptr + GPT9_CLKSEL_OFFSET, tmp32);
munmap_device_io(clk_ptr, PG_SZ);

// turn on clks to PWM10 and PWM11
clk_ptr = mmap_device_io(PG_SZ, CM_CLK_BASE);
tmp32 = in32(clk_ptr + FCLK_ENABLE_OFFSET);
tmp32 |= EN_GPT10 | EN_GPT11;
out32(clk_ptr + FCLK_ENABLE_OFFSET, tmp32);
tmp32 = in32(clk_ptr + ICLK_ENABLE_OFFSET);
tmp32 |= EN_GPT10 | EN_GPT11;
out32(clk_ptr + ICLK_ENABLE_OFFSET, tmp32);
tmp32 = in32(clk_ptr + GPT10_CLKSEL_OFFSET);
tmp32 |= CLKSEL_GPT10 | CLKSEL_GPT11;
out32(clk_ptr + GPT10_CLKSEL_OFFSET, tmp32); // select 13 MHz clock
munmap_device_io(clk_ptr, PG_SZ);

// program the input GPIO pins
padconf_ptr = mmap_device_io(PG_SZ, PAD_CONF_BASE);
tmp32 = PAD_MODE4 | (PAD_MODE4<<16) | PAD_INPUTENABLE | (PAD_INPUTENABLE<<16) | PAD_PU_EN | (PAD_PU_EN<<16); // for switch inputs
out32(padconf_ptr + PAD_CTRL_G130_131, tmp32);
out32(padconf_ptr + PAD_CTRL_G136_137, tmp32);
tmp32 = PAD_MODE4 | (PAD_MODE4<<16) | PAD_INPUTENABLE | (PAD_INPUTENABLE<<16); // for photo sensor inputs
out32(padconf_ptr + PAD_CTRL_G132_133, tmp32);
tmp32 = PAD_MODE4 | (PAD_MODE4<<16) | PAD_INPUTENABLE | (PAD_INPUTENABLE<<16) | (PAD_PU_EN<<16); // for photo sensor input and run switch input
out32(padconf_ptr + PAD_CTRL_G134_135, tmp32);
// output BOOT LED
tmp32 = in32(padconf_ptr + PAD_CTRL_G138_139);
tmp32 &= 0xffff0000;
tmp32 |= PAD_MODE4;
out32(padconf_ptr + PAD_CTRL_G138_139, tmp32);
// PWM pins
tmp32 = PAD_MODE2 | (PAD_MODE2 << 16);
out32(padconf_ptr + PWM_PAD_9, tmp32);
tmp32 = in32(padconf_ptr + PWM_PAD_11);
tmp32 &= 0xffff0000;
tmp32 |= PAD_MODE2;
out32(padconf_ptr + PWM_PAD_11, tmp32);
munmap_device_io(padconf_ptr, PG_SZ);

gpio_ptr = mmap_device_io(PG_SZ, GPIO5_BASE);

// set sensor GPIOs as inputs
tmp32 = in32(gpio_ptr + GPIO_OE);
tmp32 |= (1<<BUCKET_SENSOR_1)

(1<<BUCKET_SENSOR_2)
(1<<SENSOR_GPIO(0))
(1<<SENSOR_GPIO(1))
(1<<SENSOR_GPIO(2))
(1<<START_SWITCH)
(1<<PROGRAM_SWITCH_1)
(1<<PROGRAM_SWITCH_2);
out32(gpio_ptr + GPIO_OE, tmp32);

// map pwm’s
left_servo_pwm = mmap_device_io(PG_SZ, TIMER_9_BASE);
right_servo_pwm = mmap_device_io(PG_SZ, TIMER_10_BASE);
dump_servo_pwm = mmap_device_io(PG_SZ, TIMER_11_BASE);

// setup/turn off all motors
out32(left_servo_pwm + TIMER_TCLR, 0); // disable
out32(left_servo_pwm + TIMER_TLDR, SERVO_TLDR);
out32(left_servo_pwm + TIMER_TMAR, SERVO_STOP + LEFT_SERVO_TRIM);
out32(left_servo_pwm + TIMER_TCCR, SERVO_TLDR);
tmp32 = TIMER_START | TIMER_AUTORELOAD | TIMER_PT | TIMER_TRG | TIMER_CE | TIMER_SCPWM;
out32(left_servo_pwm + TIMER_TCLR, tmp32);

out32(right_servo_pwm + TIMER_TCLR, 0); // disable
out32(right_servo_pwm + TIMER_TLDR, SERVO_TLDR);
out32(right_servo_pwm + TIMER_TMAR, SERVO_STOP);
out32(right_servo_pwm + TIMER_TCCR, SERVO_TLDR);
tmp32 = TIMER_START | TIMER_AUTORELOAD | TIMER_PT | TIMER_TRG | TIMER_CE | TIMER_SCPWM;
out32(right_servo_pwm + TIMER_TCLR, tmp32);

out32(dump_servo_pwm + TIMER_TCLR, 0); // disable
out32(dump_servo_pwm + TIMER_TLDR, SERVO_TLDR);
out32(dump_servo_pwm + TIMER_TMAR, SERVO_STOP);
out32(dump_servo_pwm + TIMER_TCCR, SERVO_TLDR);
tmp32 = TIMER_START | TIMER_AUTORELOAD | TIMER_PT | TIMER_TRG | TIMER_CE | TIMER_SCPWM;
out32(dump_servo_pwm + TIMER_TCLR, tmp32);
// turn on BOOT LED
}

void dinit()
{
munmap_device_io(gpio_ptr, PG_SZ);
munmap_device_io(left_servo_pwm, PG_SZ);
munmap_device_io(right_servo_pwm, PG_SZ);
munmap_device_io(dump_servo_pwm, PG_SZ);
}

void set_motor(int motor_num, int dir) // dir: -1 = back, 0 = stop, 1 = fwd
{
switch(motor_num) {
case LEFT_MOTOR:
switch(dir) {
case MOTOR_FWD:
out32(left_servo_pwm + TIMER_TMAR, SERVO_FWD);
break;
case MOTOR_STOP:
out32(left_servo_pwm + TIMER_TMAR, SERVO_STOP + LEFT_SERVO_TRIM);
break;
case MOTOR_REV:
out32(left_servo_pwm + TIMER_TMAR, SERVO_REV);
break;
}
break;
case RIGHT_MOTOR:
switch(dir) {
case MOTOR_FWD:
out32(right_servo_pwm + TIMER_TMAR, SERVO_FWD);
break;
case MOTOR_STOP:
out32(right_servo_pwm + TIMER_TMAR, SERVO_STOP);
break;
case MOTOR_REV:
out32(right_servo_pwm + TIMER_TMAR, SERVO_REV);
break;
}
break;
case DUMP_MOTOR:
switch(dir) {
case MOTOR_FWD:
out32(dump_servo_pwm + TIMER_TMAR, SERVO_FWD);
break;
case MOTOR_STOP:
out32(dump_servo_pwm + TIMER_TMAR, SERVO_STOP);
break;
case MOTOR_REV:
out32(dump_servo_pwm + TIMER_TMAR, SERVO_REV);
break;
}
break;
}
}

/* This returns sensor indication in bit positions as follows:

  • bit 0 bucket sensor 1 G130
  • bit 1 bucket sensor 2 G131
  • bit 2 photo sensor 1 G132
  • bit 3 photo sensor 2 G133
  • bit 4 photo sensor 3 G134
  • bit 5 run switch G G135
  • bit 6 program switch 1 G136
  • bit 7 program switch 2 G137
    */
    int read_sensors()
    {
    uint32_t gpio, ret;
    gpio = in32(gpio_ptr + GPIO_DATAIN);
    ret = (gpio >> 2) & 0xff;
    return ret;
    }

void run()
{
int sensors, i;
int tmar;
/* for (tmar = 0; tmar < 1000; tmar++) {
out32(left_servo_pwm + TIMER_TMAR, SERVO_STOP - tmar);
printf(“tmar %d\n”, tmar);
usleep(10000);
}
for (tmar = 0; tmar < 1000; tmar++) {
out32(left_servo_pwm + TIMER_TMAR, SERVO_STOP + tmar);
printf(“tmar %d\n”, tmar);
usleep(10000);
} /
do {
sensors = read_sensors();
usleep(1000);
} while ( (sensors & (1<<5)) == 0); // wait until start button is pressed
/
for (i = 1700; i <= 2000; i++) {
int level;
level = SERVO_TLDR + SYS_CLK * ( ( (float) i) / 1000000.0 );
printf(“level: %d\n”, level);
out32(left_servo_pwm + TIMER_TMAR, level );
usleep(10000);
} */
set_motor(LEFT_MOTOR, MOTOR_FWD);
set_motor(RIGHT_MOTOR, MOTOR_FWD);
set_motor(DUMP_MOTOR, MOTOR_FWD);
sleep(5);
set_motor(LEFT_MOTOR, MOTOR_REV);
set_motor(RIGHT_MOTOR, MOTOR_REV);
set_motor(DUMP_MOTOR, MOTOR_REV);
sleep(5);
set_motor(LEFT_MOTOR, MOTOR_STOP);
set_motor(RIGHT_MOTOR, MOTOR_STOP);
set_motor(DUMP_MOTOR, MOTOR_STOP);
sensors = read_sensors();
printf(“sensors: 0x%x\n”, sensors);
}

int main(int argc, char *argv[]) {
init();
run();
dinit();
return EXIT_SUCCESS;
}