AM335x unable to initialize ECAP0 from userspace

61 views
Skip to first unread message

clig...@adextechnologies.com

unread,
Jul 29, 2016, 8:35:14 AM7/29/16
to BeagleBoard
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

Charles Steinkuehler

unread,
Jul 29, 2016, 10:11:21 AM7/29/16
to beagl...@googlegroups.com
On 7/29/2016 7:06 AM, clig...@adextechnologies.com wrote:
>
> 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.

--
Charles Steinkuehler
cha...@steinkuehler.net

clig...@adextechnologies.com

unread,
Aug 3, 2016, 7:28:34 AM8/3/16
to BeagleBoard
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

TJF

unread,
Aug 3, 2016, 11:06:38 AM8/3/16
to BeagleBoard, clig...@adextechnologies.com
Hello Chris!


Am Freitag, 29. Juli 2016 14:35:14 UTC+2 schrieb clig...@adextechnologies.com:
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!

You could use libpruio.  It drives the eCAP modules in PWM mode. Check out the examples pwm_cap or pwm_adc for details. (Currently it's limited to kernel 3.8.x.)

BR
Reply all
Reply to author
Forward
0 new messages