NOTICE: The Processors Wiki will End-of-Life on January 15, 2021. It is recommended to download any files or other content you may need that are hosted on processors.wiki.ti.com. The site is now set to read only.
Omapl137 linux eqep driver
Contents
- 1 Agenda
- 2 Rotary encoder
- 3 Introduction
- 4 Rotary encoder present on OMAPL137 UI card
- 5 Rotary encoder
- 6 Working Principle
- 7 eQEP Peripheral Overview
- 8 eQEP inputs
- 9 Functional block diagram
- 10 Quadrature decoder state machine
- 11 eQEP watchdog timer
- 12 Registers
- 13 eQEP Driver on DA830/OMAPL137
- 14 Driver Configuration
- 15 Driver Overview
- 16 Application flow diagram
- 17 Example application
- 18 References
Agenda[edit]
- Rotary encoder
- eQEP Peripheral overview
- eQEP Driver and application
- References
Rotary encoder[edit]
- Introduction
- Output signals
- Working principle
Introduction[edit]
- The enhanced quadrature encoder pulse (eQEP) module is used for direct interface with a linear or rotary incremental encoder to get position, direction, and speed information from a rotating machine for use in a high-performance motion and position-control system.
Rotary encoder present on OMAPL137 UI card[edit]
Rotary encoder[edit]
Working Principle[edit]
- As the disk rotates, the two photo-elements generate signals that are shifted 90 degrees out of phase from each other. These are commonly called the quadrature QEPA and QEPB signals
- A second track is added to generate a signal that occurs once per revolution (QEPI: Signal to indicate the index or end of one revolution), which can be used to indicate an absolute position
- The clockwise direction for most encoders is defined as the QEPA channel going positive before the QEPB channel and vise versa as shown in figure
eQEP Peripheral Overview[edit]
- eQEP inputs
- Functional block diagram
- Quadrature decoder state machine
- eQEP watchdog timer
eQEP inputs[edit]
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.
- QEPA/XCLK and QEPB/XDIR: These two pins can be used in quadrature-clock mode or direction-count mode
- Quadrature-clock Mode: The eQEP encoders provide two square wave signals (A and B) 90 electrical degrees out of phase
- Direction-count Mode: In direction-count mode, direction and clock signals are provided directly from the external source. Some position encoders have this type of output instead of quadrature output. The QEPA pin provides the clock input and the QEPB pin provides the direction input
- QEPI: Index or Zero Marker
- QEPS: Strobe Input: This general-purpose strobe signal can initialize or latch the position counter on the occurrence of a desired event on the strobe pin. This signal is typically connected to a sensor or limit switch to notify that the motor has reached a defined position
Functional block diagram[edit]
The eQEP peripheral contains the following major functional units (as shown in Figure):
- Programmable input qualification for each pin (part of the GPIO MUX)
- Quadrature decoder unit (QDU)
- Position counter and control unit for position measurement (PCCU)
- Quadrature edge-capture unit for low-speed measurement (QCAP)
- Unit time base for speed/frequency measurement (UTIME)
- Watchdog timer for detecting stalls (QWDOG)
Quadrature decoder state machine[edit]
- The direction decoding logic of the eQEP circuit determines which one of the sequences (QEPA, QEPB) is the leading sequence and accordingly updates the direction information in the QDF bit in the eQEP status register (QEPSTS). Both edges of the QEPA and QEPB signals are sensed to generate count pulses for the position counter.
eQEP watchdog timer[edit]
- Contains a 16-bit watchdog timer to monitor the quadrature-clock to indicate proper operation of the motion-control system.
- Clocked from SYSCLKOUT/64 and the quadrate clock event (pulse) resets the watchdog timer.
- If no quadrature-clock event is detected until a period match (QWDPRD = QWDTMR), then the watchdog timer will time out and the watchdog interrupt flag will be set (QFLG[WTO]).
- The time-out value is programmable through the watchdog period register (QWDPRD)
Registers[edit]
- Here are the registers as part of eQEP peripheral. For more information on the registers, refer to eQEP datasheet.
eQEP Driver on DA830/OMAPL137[edit]
- Configuration
- Overview
- Application flow diagram
- Example application
Driver Configuration[edit]
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[edit]
- Driver provides very low level interface of the peripheral to the user
- On each interrupt, the driver sends up all of the latched register values along with the values of the interrupt flag and status registers.
- User needs to have good understanding of the external encoder used and also about the functioning of the eQEP peripheral and its registers.
- Application needs to configure the driver with required register settings through sysfs entries to operate with the device.
Application flow diagram[edit]
- Here is the flow diagram of one of the application shared in the next slide
Example application[edit]
- The example application interfaces with the rotary encoder (RotaryEncoder MCV 290) present on the UI card.
- Here is the test application
- C code -
<syntaxhighlight lang="c"> /*
* 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); }
</syntaxhighlight>
- The application flow for the application is already shown in the previous slide
- The application will display the register values of the eQEP0 (instance 0) peripheral, when user rotates the rotary encoder present on the OMAPL137/DA830 UI card interfaced to eQEP0 instance. The registers that are displayed are:
- QFLG (Interrupt Flag Register)
- qepsts (Status Register)
- qposlat (Position counter latch Register)
- qposilat (Index position latch Register)
- qposslat (Strobe position latch Register)
- qctmrlat (Capture position latch Register)
- qcprdlat (Capture period latch Register)