Omapl137 linux eqep driver

From Texas Instruments Embedded Processors Wiki

Jump to: navigation, search
Translate this page to   

Contents

Agenda


Rotary encoder


Introduction

RotaryEncoder.jpg

Rotary encoder present on OMAPL137 UI card

Ti omapl137 evm.jpg

Rotary encoder

RotaryEncoderWaveforms.jpg

Working Principle

Foward-reversemovement.jpg

eQEP Peripheral Overview


eQEP inputs

The OMAPL137/DA830 has 2 eQEP instances on chip. The inputs for each of these eQEP peripheral include two pins for quadrature-clock mode or direction-count mode, an index (or 0 marker), and a strobe input.


Functional block diagram

EqepFunctionalBlockDiagram.jpg

The eQEP peripheral contains the following major functional units (as shown in Figure):

Quadrature decoder state machine

QuadratureDecoderStateMachine.jpg

eQEP watchdog timer

EQEPWatchdog.jpg

Registers

EQEPRegisters.jpg

eQEP Driver on DA830/OMAPL137


Driver Configuration

System Type ---> 
   [*] DA830/OMAP-L137 UI (User Interface) board support Device Drivers --->   
SPI support ---> 
   [ ] SPI support Input device support ---> 
[*] Miscellaneous devices ---> 
   <*> TI enhanced Quadrature Encoder Pulse (eQEP) support

Note: The Rotary Encoders are present on the UI card, so the UI Card needs to be enabled


Driver Overview


Application flow diagram

EQEPflowchart.JPG

Example application

/*
 * Listen for events from TI eQEP attached to a volume control knob.
 *
 * Author: Mark A. Greer <mgreer@mvista.com>
 *
 * 2008 (c) MontaVista Software, Inc. This file is licensed under
 * the terms of the GNU General Public License version 2. This program
 * is licensed "as is" without any warranty of any kind, whether express
 * or implied.
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/fcntl.h>
#include <linux/input.h>
 
#define EQEP_DEFAULT_DEVICE	"/dev/input/event0"
#define EQEP_SYSFS_DEVICE	"/sys/devices/platform/eqep"
 
#define ARRAY_SIZE(x)	(sizeof(x) / sizeof((x)[0]))
 
typedef unsigned long	u32;
 
static int fd;
 
struct eqep_event_info {
	struct input_event qflg_ev;
	struct input_event qepsts_ev;
	struct input_event qposlat_ev;
	struct input_event qposilat_ev;
	struct input_event qposslat_ev;
	struct input_event qctmrlat_ev;
	struct input_event qcprdlat_ev;
	struct input_event sync_ev;
} __attribute__((packed));
 
 
static void print_usage(char *argv[])
{
	fprintf(stderr, "Usage: %s [<input device>] - default dev: %s\n",
			basename(argv[0]), EQEP_DEFAULT_DEVICE); }
 
enum {
	QPOSCNT,
	QPOSINIT,
	QPOSMAX,
	QPOSCMP,
	QUTMR,
	QUPRD,
	QWDTMR,
	QWDPRD,
	QDECCTL,
	QEPCTL,
	QCAPCTL,
	QPOSCTL,
	QCTMR,
	QCPRD,
	REVID,
	QEINT,
};
 
struct sysdev_info {
	char	*path;
	int	init_required;
	u32	init_val;
};
 
static struct sysdev_info sysdev_tab[] = {
	[QPOSCNT] = {
		.path		= "qposcnt",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QPOSINIT] = {
		.path		= "qposinit",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QPOSMAX] = {
		.path		= "qposmax",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QPOSCMP] = {
		.path		= "qposcmp",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QUTMR] = {
		.path		= "qutmr",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QUPRD] = {
		.path		= "quprd",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QWDTMR] = {
		.path		= "qwdtmr",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QWDPRD] = {
		.path		= "qwdprd",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QDECCTL] = {
		.path		= "qdecctl",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QEPCTL] = {
		.path		= "qepctl",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QCAPCTL] = {
		.path		= "qcapctl",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QPOSCTL] = {
		.path		= "qposctl",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QCTMR] = {
		.path		= "qctmr",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[QCPRD] = {
		.path		= "qcprd",
		.init_required	= 1, 
		.init_val	= 0xdeadbeef,
	},
	[REVID] = {
		.path		= "revid",
		.init_required	= 0, 
	},
	[QEINT] = {
		.path		= "qeint",
		.init_required	= 0, 
	},
};
 
static int read_eqep_event(int fd)
{
	ssize_t cnt;
	struct eqep_event_info eei;
 
	/* read() should be in loop to collect all data--or use fread() */
	cnt = read(fd, &eei, sizeof(eei));
	if (cnt != sizeof(eei))
		return -1;
 
	printf("%d:%d:\n", eei.qflg_ev.time.tv_sec, 
eei.qflg_ev.time.tv_usec);
	printf("  %d %d qflg:\t0x%08x\n", eei.qflg_ev.type,
			eei.qflg_ev.code, eei.qflg_ev.value);
	printf("  %d %d qepsts:\t0x%08x\n", eei.qepsts_ev.type,
			eei.qepsts_ev.code, eei.qepsts_ev.value);
	printf("  %d %d qposlat:\t0x%08x\n", eei.qposlat_ev.type,
			eei.qposlat_ev.code, eei.qposlat_ev.value);
	printf("  %d %d qposilat:\t0x%08x\n", eei.qposilat_ev.type,
			eei.qposilat_ev.code, eei.qposilat_ev.value);
	printf("  %d %d qposslat:\t0x%08x\n", eei.qposslat_ev.type,
			eei.qposslat_ev.code, eei.qposslat_ev.value);
	printf("  %d %d qctmrlat:\t0x%08x\n", eei.qctmrlat_ev.type,
			eei.qctmrlat_ev.code, eei.qctmrlat_ev.value);
	printf("  %d %d qcprdlat:\t0x%08x\n", eei.qcprdlat_ev.type,
			eei.qcprdlat_ev.code, eei.qcprdlat_ev.value);
	printf("  %d %d sync:\t0x%08x\n", eei.sync_ev.type,
			eei.sync_ev.code, eei.sync_ev.value);
	return 0;
}
 
static int read_all_events(int fd)
{
	fd_set rfds;
	struct timeval tv;
	int rc;
 
	FD_ZERO(&rfds);
	FD_SET(fd, &rfds);
 
	for (;;) {
		tv.tv_sec = 0;
		tv.tv_usec = 0;
 
		rc = select(fd + 1, &rfds, NULL, NULL, &tv);
		if (rc < 0) {
			fprintf(stderr, "select() failed: %s (%d)\n",
					strerror(errno), errno);
			return -1;
		}
 
		if (!FD_ISSET(fd, &rfds))
			break;
 
		rc = read_eqep_event(fd);
		if (rc < 0) {
			fprintf(stderr, "read() failed: %s (%d)n",
					strerror(errno), errno);
			return -1;
		}
	}
 
	return 0;
}
 
void sigio_handler(int sig)
{
	int rc;
 
	if (sig == SIGIO) {
		rc = read_all_events(fd);
		if (rc != 0)
			exit(100);
	} else
		fprintf(stderr, "Bogus signal: %d\n", sig); }
 
int write_reg(struct sysdev_info *sip, int unit, u32 val) {
	int lfd;
	unsigned long v;
	char fn[64], s[16];
 
	sprintf(fn, "%s.%d/%s", EQEP_SYSFS_DEVICE, 0, sip->path);
 
	lfd = open(fn, O_WRONLY);
	if (lfd < 0) {
		fprintf(stderr, "Can't open '%s', %s (%d)\n", fn,
				strerror(errno), errno);
		return -1;
	}
 
	sprintf(s, "0x%08x", val);
 
	write(lfd, s, strlen(s));
 
	close(lfd);
	return 0;
}
 
int config_hw(int fd)
{
	int i, lfd;
	ssize_t cnt;
	char fn[64], s[64];
 
	write_reg(&sysdev_tab[QPOSINIT], 0, 0x0);
	write_reg(&sysdev_tab[QPOSMAX], 0, 0xffffffff);
	write_reg(&sysdev_tab[QPOSMAX], 0, 0x1);
 
	/* XXX Interrupt evey few seconds (50MHz) */
	write_reg(&sysdev_tab[QUPRD], 0, 5*50*1024*1024);
 
	write_reg(&sysdev_tab[QDECCTL], 0, 0x0);
	write_reg(&sysdev_tab[QEPCTL], 0, 0xd0be);
	write_reg(&sysdev_tab[QEPCTL], 0, 0x50ba);
	write_reg(&sysdev_tab[QEINT], 0, 0x0868);
	write_reg(&sysdev_tab[QEINT], 0, 0x0068);
 
	return 0;
}
 
int main(int argc, char *argv[])
{
	int rc;
	long buf;
	char *dev;
	struct sigaction sa;
 
	switch (argc) {
	case 1:
		dev = EQEP_DEFAULT_DEVICE;
		break;
	case 2:
		dev = argv[1];
		break;
	default:
		print_usage(argv);
		exit(1);
	}
 
	sa.sa_handler = sigio_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
 
	rc = sigaction(SIGIO, &sa, NULL);
	if (rc == -1) {
		fprintf(stderr, "sigaction failed: %s (%d)\n",
				strerror(errno), errno);
		exit(2);
	}
 
	rc = config_hw(fd);
	if (rc < 0) {
		fprintf(stderr, "config_hw() failed.  I quit.\n");
		exit(7);
	}
 
	fd = open(dev, O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open '%s', %s (%d)\n", dev,
				strerror(errno), errno);
		exit(3);
	}
 
	/* Adding O_ASYNC & O_NONBLOCK in open() doesn't work */
	rc = fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK);
	if (rc < 0) {
		fprintf(stderr, "fcntl() failed: %s (%d)\n",
				strerror(errno), errno);
		exit(4);
	}
 
	rc = fcntl(fd, F_SETOWN, getpid());
	if (rc < 0) {
		fprintf(stderr, "fcntl() failed: %s (%d)\n",
				strerror(errno), errno);
		exit(5);
	}
 
	rc = ioctl(fd, EVIOCGBIT(EV_MSC, MSC_MAX), &buf);
	if (!rc) {
		fprintf(stderr, "ioctl failed, %s (%d)\n",
				strerror(errno), errno);
		exit(6);
	} else
		printf("  0x%08x\n", buf);
 
	for (;;)
		sleep(24 * 3600);
 
	/* NOTREACHED */
	close(fd);
}

References

E2e.jpg For technical support on OMAP please post your questions on The OMAP Forum. Please post only comments about the article Omapl137 linux eqep driver here.
Hyperlink blue.png Links
ARM Microcontroller MCU ARM Processor Digital Media Processor Digital Signal Processing Microcontroller MCU Multi Core Processor
Ultra Low Power DSP 8 bit Microcontroller MCU 16 bit Microcontroller MCU 32 bit Microcontroller MCU

Leave a Comment
Personal tools
Namespaces
Variants
Actions
Navigation
Print/export
Toolbox