Difference between revisions of "Using the LS7166 Quadrature Counter"
(→Code) |
|||
Line 203: | Line 203: | ||
* |
* |
||
* Creation date: 20-APR-2010 |
* Creation date: 20-APR-2010 |
||
* Last modified: |
* Last modified: 21-APR-2010 |
||
* |
* |
||
* Thomas Peterson |
* Thomas Peterson |
Revision as of 00:16, 22 April 2010
Introduction
The LS7166 is a powerful quadrature decoder/counter chip. A motor's encoder can be connected directly to the LS7166, which will decode and count pulses as the encoder channels change. Using a parallel data bus, a microcontroller can simply read the current count value from the chip, allowing a project to interface with motor encoders without using any additional timers.
In this document, the LS7166 is interfaced with a PIC32, but the code could be ported to work with a PIC18.
Overview of the LS7166
The LS7166 is a 24 bit quadrature counter. The A and B inputs normally serve as quadrature inputs, like those which come from a quadrature encoder like those on most motors. The A and B inputs can also server as up / down counter inputs, so that the LS7166 can be used for other applications as well. The internal counter can count in binary, BCD, or in time units (hours/minutes/seconds). In any motor-related application, binary counting is necessary. The chip can also operate in divide-by-n mode, which loads the count register with a fixed value on rollover. This is useful for motor applications, since the fixed value can be set to the number of pulses in a full rotation, and then whenever the count value is read, the exact position of the motor can be known no matter the amount of time that has passed since the last read. The chip has several other features that can be read about in the datasheet. The code in this document only covers divide-by-n binary quadrature mode, and for more advanced features the support code will need to be modified.
The LS7166 communicates with a microcontroller via a bi-directional 8-bit data bus and several control bits. Four of the contorl bits, /RD (read), /WR (write), C/D (control or data), and /CS (chip select), are required for communication. Two other control lines, LCTR/LLTC and ABGT/RCTR are for more complex features and can usually be tied to Vdd or GND as needed. There are also two pins which output flags when programmed events occur, such as an overflow. For more info on these features, see the datasheet. Note that most pins are active low, and thus must be kept at high at all times unless in use. If multiple chips are used (for interfacing to multiple motors), all the communication lines can be reused except /CS (chip select).
The following is a list of registers and the general use of each, even though they are already handled by the code written in a later section.
- MCR- Master control register. This register controls functions like resetting the chip and register transfer operations. (Write only)
- CNTR- Counter. This register contains the current count value. CNTR cannot be read to or written from directly. Instead PR or OR must be used.
- PR- Preset register. This register contains a value that can be written to CNTR by software control or on underflow in the divide-by-n mode. As such, it generally will contain the maximum encoder value. (Write only)
- ICR- Input control register. This register sets up input writing modes. (Write only)
- OSR- Output status register. This register contains some status bits. (Read only)
- OCCR- Output control register. This register sets up output modes. (Write only)
- QR- Quadrature register. This register sets up the quadrature mode. (Write only)
- OL- Output latch. This register is used to read the value of CNTR. A write of 0x03 to MCR will transfer CNTR to OL, and then reading OL will obtain the CNTR value. (Read only)
Circuit
The LS7166, as mentioned above, connects to the PIC via an 8-bit data bus, 4 control bits, 2 optional control bits, and 2 optional flags. In the code below and example schematic, pins B0-B7 are used for the data bus. These pins can be changed, but the data bus read and write functions will need to be changed to interface with bit lines instead of bytes. Since this was put in the separate function, other code won't have to be changed, other than TRIS setups.
Other than PIC connections, the LS7166 also needs power (3.3v or 5v), ground, and connections to the A and B inputs of the encoder. Also note that in the case of this example, the optional control bit /ABGT is tied to ground, and the optional control bit /LCTR is tied to the flag /CY.
As mentioned above, multiple LS7166 can be used which are connected to the same data and control lines, except for chip select, which needs to be separate for each chip. The code will also have to be modified to toggle specific /CS lines during reads.
Code
NOTE: this code still has a slight bug: at overflow the CNTR doesn't roll to 0. This will be fixed soon
The code consists of three files:
- 7166.h - This header file contains function definitions, pin defines, etc.
- 7166_lib.c - This c file contains functions that interface with the 7166
- 7166_test.c - This c file contains the main function and is a simple example of how to use the library functions
All three can be download from this zip file.
7166_lib.c
This c file contains various functions used to interface with the LS7166. The init_7166() function should be called before using any other functions. The init function can be modified to utilize other special features of the chip not used here. Note that at the end of the init function, the counter limit value is loaded into the PR register. This number should be changed based on the full rotation count of the encoder in use.
The other primary function that will be used is read_reg(OL), which returns the value from CNTR after transferring CNTR to OL. Most other functions will usually not be needed unless special features are needed.
/******************************************************************** * 7166_lib.c : Library functions for using the 7166R SPI decoder * * Creation date: 20-APR-2010 * Last modified: 22-APR-2010 * * Thomas Peterson * LIMS ********************************************************************/ #include <plib.h> #include <peripheral/generic.h> #include <peripheral/spi.h> #include "7166.h" /* Comm_dir: Switches tris bits on comm bus to inputs or outputs If the bus is changed from port B to other pins, this fcn will need to be changed */ void comm_dir(int dir) { if (dir==IN) { //Set as input TRISB |= 0x00FF; } else { //set as output TRISB &= 0xFF00; } } /* bus_out : This function sends out a byte on the data bus If the bus pins are changed, this fcn will need to be changed too. This does NOT handle other control lines. */ void bus_out(unsigned char byte) { comm_dir(OUT); LATB = byte; } /* bus_in : This function reads a byte on the data bus If the bus pins are changed, this fcn will need to be changed too. This does NOT handle other control lines. */ unsigned char bus_in() { comm_dir(IN); return (unsigned char)(PORTB & 0x00FF); } void init_7166() { write_ctrl(MCR,0x20); //Performs master reset write_ctrl(MCR,0x04); //Sub-reset write_ctrl(ICR,0x18); //Enables A/B, sets up pin 3-4 write_ctrl(OCCR, 0x34); //Divide by n mode 0x04 write_ctrl(QR,0x03); //x4 quadrature mode 0x04 //0x3840->Pr write_PR(0x3840); //Upper limit } /* write_ctrl: writes a byte to the given control register */ void write_ctrl(unsigned char reg, unsigned char byte){ NCS = 0; //Select chip NRD = 1; C_ND = 1; //Setup to write to control register unsigned char op = (reg << 6) | byte; bus_out(op); latchWR(); //write value NCS = 1; //Unselect chip } void latchWR() { delay(); //Setup time (???) NWR = 0; //Latch out write delay(); //Hold time (???) NWR = 1; //End write } void write_PR(unsigned int value) { unsigned int write_value; //Prepare for PR read write_ctrl(MCR,0x01); //Reset ctr NCS = 0; //select chip C_ND = 0; //Writes will be to PR _only_ write_value = value & 0xff; //Get lowest byte bus_out((unsigned char)write_value); latchWR(); //Latch out value write_value = (value >> 8) & 0xff; //Get next byte bus_out((unsigned char)write_value); latchWR(); //Latch out value write_value = (value >> 16) & 0xff; //Get MSB bus_out((unsigned char)write_value); latchWR(); //Latch out value NCS = 1; //Unselect chip write_ctrl(MCR,0x08); //PR->CNTR } unsigned int read_reg(unsigned char reg) { unsigned int read_value; if (reg == OSR) { NCS = 0; //Select chip C_ND = 1; NWR = 1; read_value = latchRD(); NCS = 1; //Deselect chip return read_value; } if (reg == OL) { write_ctrl(MCR,0x03); //Prepare for read C_ND = 0; NRD = 1; NWR = 1; NCS = 0; //Select chip unsigned char read_byte; read_byte = latchRD(); //Read byte 0 read_value = read_byte; delay(); read_byte = latchRD(); //REad byte 1 int tmp = read_byte; tmp = tmp << 8; read_value |= tmp; delay(); read_byte = latchRD(); //Read byte 3 tmp = read_byte; tmp = tmp << 16; read_value |= tmp; NCS = 1; //Unselect chip return read_value; } //no other regs can be read } unsigned char latchRD() { NRD = 0; //Read delay(); //??? unsigned char read_byte = bus_in(); delay(); NRD = 1; //End latch return read_byte; } /* Delays for 10 cycles, plus branch time */ void delay() { asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); }
7166_test.c
This c file simply loops continously and reads values from the LS7166. Note that to use the 7166, the only steps are to setup the pins, call the init function, then call the read function to get a value.
/*********************************************************** * 7366_test.c : C-file which runs tests that use the * LS7166 parallel-interface encoder chip. * * Creation date: 20-APR-2010 * Last modified: 21-APR-2010 * * Thomas Peterson * LIMS ********************************************************************/ #include "HardwareProfile.h" #include "7166.h" #include <plib.h> #pragma config FPBDIV = DIV_1 //Sets PBCLK to SYSCLK #define FPB 80000000 void setup_rs232(int pbClk) { #define config1 UART_EN | UART_IDLE_CON | UART_RX_TX | UART_DIS_WAKE | UART_DIS_LOOPBACK | UART_DIS_ABAUD | UART_NO_PAR_8BIT | UART_1STOPBIT | UART_IRDA_DIS | UART_DIS_BCLK_CTS_RTS| UART_NORMAL_RX | UART_BRGH_SIXTEEN #define config2 UART_TX_PIN_LOW | UART_RX_ENABLE | UART_TX_ENABLE | UART_INT_TX | UART_INT_RX_CHAR | UART_ADR_DETECT_DIS | UART_RX_OVERRUN_CLEAR // Open UART2 with config1 and config2 OpenUART2( config1, config2, pbClk/16/9600-1); // calculate actual BAUD generate value. putsUART2("UART init done\r\n"); } void Delayms( unsigned t) // This uses Timer 1, can be changed to another timer. Assumes FPB = SYS_FREQ { OpenTimer1(T1_ON | T1_PS_1_256, 0xFFFF); while (t--) { // t x 1ms loop WriteTimer1(0); while (ReadTimer1() < FPB/256/1000); } CloseTimer1(); } // Delayms /* Main Function */ int main(void) { // Configure the proper PB frequency and the number of wait states int pbClk = SYSTEMConfigPerformance(SYS_FREQ); // Set all analog pins to be digital I/O AD1PCFG = 0xFFFF; //Init I/O //Set comm bits as outputs TRISD &= 0xFFE1; //D4-D1 outputs (0) TRISC &= 0xFFE1; //C4-C1 outputs (0) //TRISB (data bus) handled by comm fcns //Set outputs high (inactive) NWR = 1; NCS = 1; C_ND = 1; NRD = 1; //UART setup setup_rs232(pbClk); //7166 setup init_7166(); //Read peroidically and send encoder value unsigned int readval; char string[40]; while(1) { Delayms(250); readval = read_reg(OL); sprintf(string,"Read from OL: %x , OSR \r\n", readval ); putsUART2(string); } }