AM335x unable to initialize ECAP0 from userspace

Good afternoon,

I would like to use the ECAP0 interface for timing a PWM duty cycle, essentially what is described in section 15.3.3.4 of the Sitara Technical Reference Manual (p.2420). I’m using a BeagleBone Black with Ubuntu 16.04 LTS. From my understanding, there are no Linux drivers available for the ECAP feature. Despite several attempts, I am not able to configure the ECAP0 as either a APWM output or an ECAP0 input.

To get started, I decided to first configure the ECAP0 interface as a APWM signal in order to test that I am properly reading and writing to the ECAP registers. Following the register settings on Table 15-93 “ECAP Initialization for APWM Mode”, I created a simple c program to write and verify these register values. First, I enable and configure the PWMSSO clock, then I write to the correct registers for initializing the PWM. Here’s my code

#define HWREG(x) (*((volatile unsigned )(x)))
#define HWREGH(x) (
((volatile unsigned short *)(x)))
#define MMAP_OFFSET (0x44C00000)
#define MMAP_SIZE (0x00001000)

// Clock Module Memory Registers
#define CM_PER (0x44E00000)
#define CM_PER_EPWMSS1_CLKCTRL (0xCC)
#define CM_PER_EPWMSS0_CLKCTRL (0xD4)
#define CM_PER_EPWMSS2_CLKCTRL (0xD8)
#define CM_PER_EPWMSS0_CLKCTRL_MODULEMODE_ENABLE (0x02)

// PWMSS0 Memory Registers
#define PWMSS0 (0x48300000)
#define PWMSS0_CLOCKCONFIG_REG (0x08)
#define ECAP0 (0x100)
#define ECAP0_TSCTR_REG (ECAP0 + 0x00)
#define ECAP0_CTRPHS_REG (ECAP0 + 0x04)
#define ECAP0_CAP1_REG (ECAP0 + 0x08)
#define ECAP0_CAP2_REG (ECAP0 + 0x0C)
#define ECAP0_CAP3_REG (ECAP0 + 0x10)
#define ECAP0_CAP4_REG (ECAP0 + 0x14)
#define ECAP0_ECCTL2_REG (ECAP0 + 0x2A)

#define handle_error(msg) \

do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main() {

// Map memory address
int memfd;
if((memfd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
return -1;
printf("/dev/mem opened\n");

// Map clock management memory
void *cm_per_map_base;
cm_per_map_base =(void *) mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, CM_PER);
if (cm_per_map_base == MAP_FAILED) {
handle_error(“mmap”);
}

printf(“cm_per_map_base memory mapped\n”);

// Enable the PWMSSO
printf(“Enable the PWMSSO …\n”);
HWREG(cm_per_map_base + CM_PER_EPWMSS0_CLKCTRL) |= CM_PER_EPWMSS0_CLKCTRL_MODULEMODE_ENABLE;
printf(“clock management reg = 0x%08x\n”, HWREG(cm_per_map_base + CM_PER_EPWMSS0_CLKCTRL));

// Unmap clock management memory
munmap(cm_per_map_base, MMAP_SIZE);

// Map peripheral memory
void *pwmss0_per_map_base;
pwmss0_per_map_base =(void *) mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, PWMSS0);
if (pwmss0_per_map_base == MAP_FAILED) {
handle_error(“mmap”);
}
printf(“pwmss0_per_map_base memory mapped\n”);

// Configure the clocking
printf(“Configure the PWM clock\n”);
HWREG(pwmss0_per_map_base + PWMSS0_CLOCKCONFIG_REG) |= 0x101;
printf(“pwm clock config reg = 0x%08x\n”, HWREG(pwmss0_per_map_base + PWMSS0_CLOCKCONFIG_REG));

// Configure ECCTRL to output simple PWM signal
printf(“Configure the PWM output\n”);
HWREGH(pwmss0_per_map_base + ECAP0_ECCTL2_REG) = 0x2C6;
printf(“ECCTL2 reg = 0x%04x\n”, HWREGH(pwmss0_per_map_base + ECAP0_ECCTL2_REG));

// Write to cap 1-4 registers
HWREG(pwmss0_per_map_base + ECAP0_CAP1_REG) = 0x1000;
printf(“CAP1 reg = 0x%04x\n”, HWREG(pwmss0_per_map_base + ECAP0_CAP1_REG));
HWREG(pwmss0_per_map_base + ECAP0_CAP2_REG) = 0x300;
printf(“CAP2 reg = 0x%04x\n”, HWREG(pwmss0_per_map_base + ECAP0_CAP2_REG));
HWREG(pwmss0_per_map_base + ECAP0_CAP3_REG) = 0x1000;
printf(“CAP3 reg = 0x%04x\n”, HWREG(pwmss0_per_map_base + ECAP0_CAP3_REG));
HWREG(pwmss0_per_map_base + ECAP0_CAP4_REG) = 0x300;
printf(“CAP4 reg = 0x%04x\n”, HWREG(pwmss0_per_map_base + ECAP0_CAP4_REG));

// Set TSCTR to zero
HWREG(pwmss0_per_map_base + ECAP0_TSCTR_REG) = 0;
printf(“TSCTR reg = 0x%04x\n”, HWREG(pwmss0_per_map_base + ECAP0_TSCTR_REG));

// Set CTRPHS to zero
HWREG(pwmss0_per_map_base + ECAP0_CTRPHS_REG) = 0;
printf(“CTRPHS reg = 0x%04x\n”, HWREG(pwmss0_per_map_base + ECAP0_CTRPHS_REG));

// Set ECCTRL to run
printf(“Enable the PWM output\n”);
HWREGH(pwmss0_per_map_base + ECAP0_ECCTL2_REG) = 0x2D6;
printf(“ECCTL2 reg = 0x%04x\n”, HWREGH(pwmss0_per_map_base + ECAP0_ECCTL2_REG));

// Unmap peripheral memory
munmap(pwmss0_per_map_base, MMAP_SIZE);

return 0;

}

Here’s the output on screen:

/dev/mem opened
cm_per_map_base memory mapped
Enable the PWMSSO …
clock management reg = 0x00000002
pwmss0_per_map_base memory mapped
Configure the PWM clock
pwm clock config reg = 0x00000111
Configure the PWM output
ECCTL2 reg = 0x02c6
CAP1 reg = 0x1000
CAP2 reg = 0x0300
CAP3 reg = 0x1000
CAP4 reg = 0x0300
TSCTR reg = 0x0000
CTRPHS reg = 0x0000
Enable the PWM output
ECCTL2 reg = 0x02d6

I’m able to successfully read, write, and verify the expected values, but I am not seeing any output on my scope. I’ve also tried creating a device tree overlay for the ECAP interface, but it does not appear to be properly setting the pin mmode to 0x00. Here’s my .dts file:

/dts-v1/;
/plugin/;

/{
compatible = “ti,beaglebone”, “ti,beaglebone-black”;
part-number = “BB-ECAP0”;
version = “00A0”;

fragment@0 {
target = <&am33xx_pinmux>;

overlay {

ecap_example: BB_ECAP0 {
pinctrl-single,pins = <
0x164 0x00 // P9_42A PINS$89 GPIO0_7 = 7 Output Mode0 pulldown

/* OUTPUT GPIO(mode7) 0x07 pulldown, 0x17 pullup, 0x?f no pullup/down /
/
INPUT GPIO(mode7) 0x27 pulldown, 0x37 pullup, 0x?f no pullup/down */

;

};
};
};

fragment@1 {
target = <&ocp>;
overlay {
ecap_helper {
compatible = “ecap-helper”;
status = “okay”;
pinctrl-names = “default”;
pinctrl-0 = <&ecap_example>;

};
};
};

};

Here’s the command line output loading and checking the pin mode:
root@arm:/lib/firmware# echo BB-ECAP0 > $SLOTS
root@arm:/lib/firmware# dmesg | tail
[ 1062.781695] bone_capemgr bone_capemgr: Using override eeprom data at slot 4
[ 1062.781737] bone_capemgr bone_capemgr: slot #4: ‘Override Board Name,00A0,Override Manuf,EBB-GPIO-Example’
[ 1062.800286] gpio-of-helper ocp:gpio_helper: could not find pctldev for node /ocp/interrupt-controller@48200000, deferring probe
[ 1062.801085] bone_capemgr bone_capemgr: slot #4: dtbo ‘EBB-GPIO-Example-00A0.dtbo’ loaded; overlay id #0
[ 1936.700092] bone_capemgr bone_capemgr: Removed slot #4
[ 3529.843681] bone_capemgr bone_capemgr: part_number ‘BB-ECAP0’, version ‘N/A’
[ 3529.843748] bone_capemgr bone_capemgr: slot #5: override
[ 3529.843786] bone_capemgr bone_capemgr: Using override eeprom data at slot 5
[ 3529.843827] bone_capemgr bone_capemgr: slot #5: ‘Override Board Name,00A0,Override Manuf,BB-ECAP0’
[ 3529.863652] bone_capemgr bone_capemgr: slot #5: dtbo ‘BB-ECAP0-00A0.dtbo’ loaded; overlay id #0
root@arm:/lib/firmware# cat $SLOTS
0: PF---- -1
1: PF---- -1
2: PF---- -1
3: PF---- -1
5: P-O-L- 0 Override Board Name,00A0,Override Manuf,BB-ECAP0
root@arm:/lib/firmware# cat $PINS | grep 964

pin 89 (44e10964.0) 00000027 pinctrl-single

I’ve been searching through the forums the past several days but have not had any success. Please let me know if you have any suggestions!

Best regards,

Chris

fragment@1 {
target = <&ocp>;
__overlay__ {
ecap_helper {
compatible = "ecap-helper";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&ecap_example>;

What is ecap-helper? The ecap target is usually something like ecap0:

https://github.com/cdsteinkuehler/beaglebone-universal-io/blob/master/cape-universaln-00A0.dts#L1432

I suspect your overlay fragment 1 isn't matching any kernel driver, so
the ecap_example pinmux isn't getting loaded. When the cape manager
is finished, you should see something like the following in dmesg:

[1206258.362090] bone-capemgr bone_capemgr.9: slot #7: Applied #23 overlays.
...
[1206259.733221] bone-capemgr bone_capemgr.9: slot #8: Applied #1 overlays.

...this is from loading my 'universal' overlay (slot 7) and the iio
overlay (slot 8). I didn't see anything like this in your dmesg
output, which leads me to think your overlay target isn't matching
anything.

Good evening Charles,

Thank you for the quick reply. I tried using your beaglebone-universal-io device tree files and I’m now able to successfully change the pin state to ‘pwm’. I’m now onto testing the PWM signal and next adding an ECAP mode for timing a PWM duty cycle.

Best regards,
Chris

Hello Chris!