Professional Documents
Culture Documents
DSA00211720
DSA00211720
DSA00211720
AN401-20
Table of Contents
SUMMARY...................................................................................................................... 3
1 ENCODERS ............................................................................................................. 3
2 HARDWARE ............................................................................................................ 7
3.2 MathEX.h......................................................................................................................................................11
3.3 Stegmann.h....................................................................................................................................................13
Summary
This application note describes how the ADMC 401 may be used solely for the purpose of calculating
position and speed information with the Stegmann SINCOS® Encoder. This application note uses the
Stegmann SCM 60 as the encoder for all calculation and hardware requirements, but any of the Stegmann
SINCOS® encoders may be used, with the relevant changes in the code.
The application note also includes all relevant software modules for serial communications with the
encoder using the Stegmann Hiperface® protocol.
1 Encoders
1.1 Standard Sinusoidal encoders
Sinusoidal encoders encode position information by providing a pair of quadrature sine and cosine signals
as the shaft is rotated. The signals may be generated by optical or magnetic means and typically produce
512 or 1024 cycles per mechanical revolution. For noise immunity the signals are typically transmitted
differentially from the encoder to the sensor interface electronics. A typical interface configuration is
shown in Figure 1.
Differential
Line Receiver
Comparators
+
+ EIA
DSP
- SIN
- Quadrature
Counter
+
+ EIB
- COS
-
Midpoint
SHA
+ Vsin
-
ADC
+ Vcos
-
In order to extract reliable position and speed information from the sinusoidal encoder signals, a certain
amount of pre-conditioning of the analogue signals must be implemented. As a first stage, the differential
SIN and COS signals (typically 1Vpp input signal range) from the sinusoidal encoder must be applied to
input differential amplifiers. This ensures maximum noise immunity and may also be used to
appropriately amplify and level shift the resultant single-ended SIN and COS signals for later input to the
Analogue to Digital Converter (ADC) stage. Next, the SIN and COS signals of Figure 1 are applied to
comparators that generate square-wave, TTL-level signals (EIA & EIB) that are synchronized to the SIN
and COS signals respectively. The relationship between the SIN, COS, EIA and EIB signals is illustrated
in Figure 2.
SIN COS
EIA
EIB
2π
ARCTAN Result
The analogue SIN and COS signals are also fed to dual sample and hold amplifiers (SHA) for subsequent
conversion to digital and post-processing in the digital signal processor (DSP). The signals EIA and EIB
are typically applied to the internal quadrature counter of a dedicated digital Encoder Interface Unit
(EIU)1 as illustrated in Figure 1. The resultant parallel word in the quadrature counter provides the crude
position estimate from the sinusoidal encoder. There are four counts per cycle of the sinusoidal
waveforms (SIN and COS) as seen in Figure 2, and so for a 512 line encoder the maximum count value is
2047 (4*512-1) which provides 11 bits of crude position information. In the case of a 1024-line encoder
the maximum count is 4095 which provides 12 bits of resolution. In the case where the EIB signal leads
the EIA signal, the encoder is determined to be rotating in the reverse direction and the quadrature count
value (EIUCNT) is decremented at each edge of the EIA and EIB signals, as seen in Figure 2. Naturally,
when EIA leads EIB, the quadrature count value is incremented at each event.
Fine position resolution is obtained by further processing the digitized SIN and COS signals to provide
much finer granularity between the EIA and EIB events, as illustrated by the lower waveform of Figure 2.
In some applications, it is necessary to know the initial position of the rotor following power up. There
are different techniques used to obtain this information depending on the particular encoder design. Some
encoder designs provide an alternative pair of sine and cosine signals that provide one cycle per
mechanical revolution, from which it is possible to derive an initial position estimate. Alternatively, some
modern sinusoidal encoders now provide a dedicated serial interface that can be used to extract the initial
position following power up.
During normal operation, the complete position information must be constructed from both the crude and
fine position information. The crude information contains the cycle identification information, which,
depending on the encoder used, can be 11 or 12 bits. The fine information is the result of the calculation
from the sinusoidal signals. With high-resolution analogue to digital conversion of 12 bits on the SIN and
COS, position information to 23/24 bit can be achieved. Since the crude position information is a
quadrature value, the 2 least significant bits (LSB) are the same as the 2 most significant bits (MSB) of
the fine data. Thus, only the 9 MSBs of the crude information are used in the case of a 512-line encoder.
In the case of a 1024 line encoder the first 10 bits are used. The construction of the final position data is
illustrated in Figure 3 for a 512-line encoder.
1
See section 1.3 for information on the operation of the EIU block.
COS SIN
Overlap Bits
Fine Position Data from SIN/COS Signals
15 0
24 0
Figure 3: Diagram of the relationship between encoder cycles and the position information
Figure 4:Diagram of the multiturn information supplied by the Stegmann encoder SCM60
The 9 bits plus the first two bits of the 5 bits of information are used to initialise the EIU count value. The
12-bit section contains the multiturn information, which is simply discarded in this example. Finally the
6-bit section contains all zero and is simply padding to fill the 32 bits.
Once this is done the analogue signals are fed into the analogue to digital converter. They are
simultaneously sampled, divided and the result is used in the ARCTAN calculation. The final result is
2
See the Stegmann’s HIPERFACE® Parameter channel booklet for more information on HIPERFACE®
combined with the EIU count value giving a 25 bits position information, as seen in Figure 5, where the
final 7 bits are padding zeros to make up the 32 information block.
EETCNT(15...0)
EIA A EIUCNT(15...0)
16-bit Quadrature EIUMAXCNT(15…0)
EIB B UP/DOWN Counter EIUCTRL(8...0)
PROGRAMMABLE EIUSTAT(7...0)
NOISE
EIZ FILTERING EISLATCH(15…0)
Z
Encoder Counter EIZLATCH(15...0)
EIS S
Control EIUFILTER(5...0)
V
θ fine = tan −1 sin (1)
Vcos
An ARCTAN calculation is carried out and the result of combined with the EIU value to produce position
information.
2 Hardware
The following describes the additional hardware necessary to 1) process the analogue signals produced by
the encoder and 2) to communicate with the encoder using the Hiperface® protocol. The analogue
hardware on the ADMC401-PB (processor board) is not discussed in this application note. To fully
understand this application note it is necessary to first read and understand the working of the analogue to
digital converter. This is explained in the Application Note: ADC-system on the ADMC401, number
AN401-53.
Figure 7: Analogue differential, gain and TTL configuration of the encoder signals
The analogue signals4, (sin, cos, refsin, refcos), produced by the encoder are differential, 1 volt peak to
peak waveforms. Thus, a differential amplifier (AD8044AR5) is required to convert them to single ended
3
This document may be download from the Analog Devices Inc. website.
4
See the Stegmann’s SCM60 datasheet at www.Stegmann.com for further information about the signals
produced by the encoder.
5
See the Analog Devices Inc. datasheet on the AD8044AR for further information.
signals suitable to feed into the ADMC401. These signals are amplified to approximately 4 volts peak to
peak to obtain the best resolution from the A/D converter. The resulting waveforms are fed into IF2A PIN
1 (Sin) and IF2A PIN 5 (Cos). See Figure 7 for the recommended circuitry. The circuit contains two
variable resistors. These are used to ensure that the sin and cos peak to peak voltages are the same and
less than or equal to 4 volts peak to peak. They should be calibrated using an oscilloscope before the
program is run.
It is possible to digitally filter the signals by making use of the fact that the ADMC401 permits 4 pairs of
signals to be simultaneously sampled. If one wishes to do this it is necessary to feed the Sin signal into
PINS 2,3,4 of IF2A and the Cos signal into PINS 6,7,8 of IF2A. Then at the very simplest, one could
acquire the average by adding the results and shifting right by 2 in the shifter block. This process has its
problems.
If the motor is running at 3000 rpm, then the encoder is producing 1536000 cycles of both Sin and Cos
waves per minute. Which is equivalent to 25.6 kHz. It takes 1.88 µs to convert all 8 A/D channels. The
time between the first sample and hold and the last is 1 µs, which means that the cycle has moved through
9.2 electrical degrees. These samples may be too far apart to be used as an average for the filter
depending on the application. In this application note it was decided not to implement this type of filter,
instead it uses only the signals samples at pins 1 and 5 of IF2A with no filtering.
ADM485AN Connection
Pin 1 Pin 26 and Pin 27 of IF1A of ADMC401
Pin 2 Pin 25 of IF1A of ADMC401
Pin 3 Pin 2 of ADM485AN
Pin 4 Pin 28 of IF1A of ADMC401
6
See the Analog Devices Inc. datasheet on the ADM485AN for further information.
Pin 5 Gnd
Pin 6 Data Positive of the encoder
Pin 7 Data Negative of the encoder
Pin 8 5 volts
Table 1: Files used with the Stegmann Hiperface® Encoder application routines
7
AN401-08: Using Serial Port 0 of the ADMC401 as a UART Interface
As with the structure from the ADIs Standard Motor Control Library, macros are defined. For this
application three macros are used for configuration and update in the code. The following table
summarises the set of macros that are defined in “stegmann.h”.
As will be seen in Section 4.2, these routines require a configuration constant, which is declared in a
dedicated section of the main include-file "main.h". If a routine requires internal configuration constants
they are declared in the associated include-file "stegmann.h". The following section will explain each of
the routines in details linked with the relevant segments of code that are found in any of the files
described in Table 3.
3.2 MathEX.h
Due to the need to calculate the parity (Check Sum) of all data sent via the serial interface to the
encoder, a library file that incorporates three XOR macros is provided.
The macro MathsEX_XORMATH2n is used to XOR two numbers or registers, and place the result in the
third parameter, usually the data memory address of Check Sum. The second macro takes one number or
register and XOR’s it with a data memory and places the result in the third parameter. The final macro takes
two data memory address and places the result of the XOR operation in the data memory address indicated
by the final parameter.
{***************************************************************************************
* *
* Library: Macros for the Stegmann Encoder *
* *
* File: MathEx.h *
* *
* Description: Math XOR library *
* Purpose : Additional XOR macros to aid in check sum calculations *
* *
* Author : JB *
* Version : 1.0 *
* Date : January 2000 *
* Modification History: None *
* *
* Embedded Control Systems *
* Analog Devices Inc. *
***************************************************************************************}
{***************************************************************************************
* *
* Other Libraries Required by this Module: *
* *
* none *
***************************************************************************************}
#ifndef mathsEx_included
#define mathsEx_included
{***************************************************************************************
* Routines Defined in this Module *
***************************************************************************************}
{ None }
{***************************************************************************************
* Global Variables Defined in this Module *
***************************************************************************************}
{ None }
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: MathsEX_XORMATH2N(%0,%1,%2); *
* *
* Get the encoder position *
* *
* Inputs : %0: Number or register *
* %1: Number or register *
* *
* Ouputs : %2: Data memory pointer (Checksum) *
* *
* Modified: AX0, AY0, AR *
* *
***************************************************************************************}
.MACRO MathsEX_XORMATH2N(%0,%1,%2);
AX0=%0;
AY0=%1;
AR=AX0 XOR AY0;
DM(%2)=AR;
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: MathsEX_XORMATH1N(%0,%1,%2); *
* *
* Get the encoder position *
* *
* Inputs : %0: Number or register *
* %1: Data memory pointer *
* *
* Ouputs : %2: Data memory pointer (Checksum) *
* *
* Modified: AX0, AY0, AR *
* *
***************************************************************************************}
.MACRO MathsEX_XORMATH1N(%0,%1,%2);
AX0=%0;
AY0=DM(%1);
AR=AX0 XOR AY0;
DM(%2)=AR;
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: MathsEX_XORMATH0N(%0,%1,%2); *
* *
* Get the encoder position *
* *
* Inputs : %0: Data memory pointer *
* %1: Data memory pointer *
* *
* Ouputs : %2: Data memory pointer (Checksum) *
* *
* Modified: AX0, AY0, AR *
* *
***************************************************************************************}
.MACRO XORMATH0N(%0,%1,%2);
AX0=DM(%0);
AY0=DM(%1);
AR=AX0 XOR AY0;
DM(%2)=AR;
.ENDMACRO;
#endif
3.3 Stegmann.h
This library includes all the macros for communicating with the Stegmann Encoder, as well as
some additional macros for manipulating some of the results.
{***************************************************************************************
* *
* Library: Macros for the Stegmann Encoder *
* *
* File: stegmann.h *
* *
* Description: Stegmann include file *
* Purpose : Macros for the Stegmann Encoder *
* *
* Author : JB *
* Version : 1.0 *
* Date : January 2000 *
* Modification History: None *
* *
* Embedded Control Systems *
* Analog Devices Inc. *
***************************************************************************************}
This constant is the address of the encoder that you wish to communicate with. 0x40 is the default address of
all Stegmann encoders.
{***************************************************************************************
* *
* Constants that need to be defined in main.h: *
* *
* .CONST addressOfEncoder = 0xxx; !The serial address of the encoder *
***************************************************************************************}
{***************************************************************************************
* *
* Other Libraries Required by this Module: *
* *
* uart0.dsp *
* adc401.dsp *
* trigono.dsp *
***************************************************************************************}
#ifndef stegmann_included
#define stegmann_included
#include <mathsEx.h>;
The two external definitions will be explained later in Stegmann.dsp. It is enough for now to say that
Stegmann_getAnalogPosition is a macro, that when called, places the analogue position value in the AR
register. The function Stegmann_Init sets up all the relevant parameters so that the ADMC401 and the
Stegmann encoder are ready to produce position information.
{***************************************************************************************
* Routines Defined in this Module *
***************************************************************************************}
.EXTERNAL Stegmann_GetAnaloguePosition_;
.EXTERNAL Stegmann_Init_;
The variable checkSum is used to hold the value of the check sum value. This is the final packet of data sent
to the encoder at the end of each instruction. It consists of all the previous packets in that instruction XORed.
{***************************************************************************************
* Global Variables Defined in this Module *
***************************************************************************************}
.EXTERNAL checkSum;
.EXTERNAL digitalPosMSB;
.EXTERNAL digitalPosLSB;
This macro combines the cycle information with the fine analogue information. Since there is a slight time
delay between getting the simultaneous samples and the getting of the EIU count, under certain conditions
combining the two words of position information would result in getting the wrong answer. For instance,
assume the encoder is moving in the forward direction and at a point where it is moving from one cycle of the
512 to another. If the analogue position is taken and lets say the result is 0x7FAA. Next the EIU count value
is read, but by now the encoder is in the new cycle and the value would be 0X0A8. Combining this
information would result in the wrong answer. This is corrected for in the algorithm as follows. The
analogue information is the most accurate and most up to date and so is not altered. Instead 1 is added to the
EIU count value that lags the analogue value. Visa versa 1 is subtracted from the EIU count that leads the
analogue value.
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_GetPosition(MSB,LSB); *
* *
* Get the encoder position *
* *
* Inputs : none *
* *
* Ouputs : %0:MSB of the mechanical position in 0.16 format(direction is clock wise)*
* %1:LSB of the mechanical position *
* *
* Modified: AX0, AX1, AY0, AY1, AR, AF, SR0, SR1 SI, SE MR0, MR1, *
* MX1, MY0, MY1, MF, I5, M5, L5 *
* *
***************************************************************************************}
.MACRO Stegmann_GetPosition(%0,%1);
.local crude_one;
.local fine_fb_zero;
.local end_of_alignment;
CALL Stegmann_GetAnaloguePosition_;
AX0 = AR;
SI=DM(EIUCNT);
SR=ASHIFT SI by -2 (hi);
AX1 = SR0;
AY1 = SR1;
AF=tstbit 15 of AX0;
IF EQ jump fine_fb_zero; !First bit of the analogue position information is zero check
AF=tstbit 14 of AX0;
IF NE jump crude_one; !Second bit of the analogue position information is one check
jump end_of_alignment; ! If neither are true then alignment is OK jump to end
crude_one:
AF=tstbit 15 of AX1; !Test first bit of the cycle information
IF NE jump end_of_alignment; !If equal to one finished, jump to end
AR=AY1-1; !If equal to zero then the cycle position is ahead,
!and must be aligned.
AY1=AR;
jump end_of_alignment; !jump to end
fine_fb_zero:
AF=tstbit 14 of AX0; !test the second bit of analogue information
IF NE jump end_of_alignment; !If equal to one jump to end of alignment
AF=tstbit 15 of AX1; !test the first bit of the cycle information
IF EQ jump end_of_alignment; !If equal to one jump to the end of alignment
AR=AY1+1; !Otherwise align by adding one to the cycle information
AY1=AR;
end_of_alignment:
AR=AX0;
SR=LSHIFT AR by 7 (lo); !Shift the analogue position information by 7 (9(2^9=512)-
2(unused))
DM(%1)=SR0; !place the lower half of the position information into the
!second parameter
.ENDMACRO;
The Stegmann_GetSpeed result is computed as a simple position difference between sample instants, in other
words, the 32-bit difference between the current and previous position information. It is left to the user to
scale the result to the appropriate format. It should be noted that a more comprehensive speed routine maybe
required, has this routine does not account for the roll over between cycles.
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_GetSpeed(New_Pos_MSB,New_Pos_LSB,Old_Pos_MSB,Old_Pos_LSB, *
* speed_MSB,speed_LSB); *
* *
* Calculates the speed *
* *
* Inputs : %0:New position information MSB (Data Memory) *
* %1:New position information LSB (Data Memory) *
* %2:Old position information MSB (Data Memory) *
* %3:Old position information LSB (Data Memory) *
* *
* Ouputs : %4:Speed information MSB (Register) *
* %5:Speed information LSB (Register) *
* *
* Modified: AX0,AX1,AY0,AY1,AR, *
* *
***************************************************************************************}
.MACRO Stegmann_GetSpeed(%0,%1,%2,%3,%4,%5);
.local Back_dir;
.local forward_dir;
.local endspeed;
{store the previous and present position and subtract them => speed}
AX0=DM(%1);
AX1=DM(%0);
AY0=DM(%3);
AY1=DM(%2);
AR=AX0-AY0;
AR=DM(EIUCTRL);
AR=tstbit 0 of AR;
IF EQ jump forward_dir;
Back_dir:
AR=AY0-AX0;
%5=AR;
AR=AY1-AX1+C-1;
%4=AR;
jump endspeed;
forward_dir:
AR=AX0-AY0;
%5=AR;
AR=AX1-AY1+C-1;
%4=AR;
endspeed:
nop;
.ENDMACRO;
Stegmann_Init_Total_Pos is used to initialise the position information. The first time, due to the length of the
algorithm it is necessary to prevent the PWM interrupt from interrupting the initialisation of the EIU count
value and communication with the encoder. This is the only time serial communication is done with the
encoder.
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_Init_Total_Pos(New_Pos_MSB,New_Pos_LSB); *
* *
* This macro gets the inital position of the encoder at startup. The encoder must be at*
* rest in order for this routine to work. *
* *
* Inputs : none *
* *
* Ouputs : %0:The initial position information MSB *
* %1:The initial position information LSB *
* *
* Modified: AX0, AX1, AY0, AY1, AR, AF, SR0, SR1, SE, SI CNTR, MR0, MR1, MF, *
* MX1, MY0, MY1, I5, M5, L5 *
* *
***************************************************************************************}
.MACRO Stegmann_Init_Total_Pos(%0,%1);
AR=IMASK;
AR=CLRBIT 9 of AR;
IMASK =AR;
CALL Stegmann_Init_;
DM(%1)=AX0;
DM(%0)=AR;
AR=IMASK;
AR=SETBIT 9 of AR;
IMASK =AR;
.ENDMACRO;
Since all communication is done in 8 bit format, storing this information in 8-bit format
would be wasteful. Stegmann_PutTogetherDM combines two on these data blocks into a single 16 bit word.
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_putTogetherDM(DM_memory,DM_memory,DM_memory); *
* *
* Since all communication is done in 8 bit format, storing this information in 8-bit *
* format would be wasteful. putTogetherDM put two on these data blocks together. *
* *
* Input: %0:data memory address or flag *
* %1:data memory address or flag *
* *
* output: %2:data memory address or flag *
* *
* Modified: SI,SR,AR,AY0 *
* *
***************************************************************************************}
.MACRO Stegmann_putTogetherDM(%0,%1,%2);
SI=DM(%0);
SR=lshift SI by 8 (LO);
AY0=DM(%1);
AR=SR0+AY0;
DM(%2)=AR;
.ENDMACRO;
The following is the implementation of functions offered by the Stegmann Encoder range. Not all the
Stegmann encoders provide all these functions. For more information on their use and structure consult the
Stegmann’s Encoder datasheet. Only one of these functions is used in the Stegmann.dsp library namely
Stegmann_read_Enc_Pos. This function is explained below. Table 3 is provided in section 3.1 which lists the
functions implemented in Stegmann.h and their relevant command number.
Stegmann_Read_Enc_Pos takes one parameter, the encoder address, which in this case is the constant
defined at the bottom of the Main.h file addressOfEncoder. First the address packet is sent. Next the
command packet is sent i.e. 0x42. Finally the checkSum packet is sent. The program then waits for the
encoder to return the encoder position 4 packets of 8 bits. This information is stored in Data Memory
address return3 through return6. Note that each time UART0_ Read_ is called AX0 holds the result. In this
case the first two calls are not stored, as they have no relevant information. The first call being the encoder
address and the second the command sent to the encoder. The final UART0_ Read_ call is not stored. It
contains the returned CheckSum value. If error checking were to be implemented then it would be necessary
to store this information. Finally, to save on space the first two packets are combined and placed in Data
Memory address digitalPosMSB, The second two packets are combined and placed in digitalPosLSB. (Note
no further macros are explained in this section. Section 3.4 is the next segment of explained code.)
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_set_position(encoder address,upper position bits,lower position bits);*
* *
* see the Stegmann data sheet for the relevant format of the position *
* *
* Inputs : %0:encoder address *
* %1:upper position bits *
* %2:lower position bits *
* *
* Ouputs : none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0 SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_set_position(%0,%1,%2);
DM(checkSum)=0;
MathsEX_XORMATH2N(%0,0x43,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X43; !Command
Call UART0_Write_;
AR=%1;
SR=ASHIFT AR by 8 (lo);
AX0=SR1; !Byte 0
MathsEX_XORMATH1N(AX0,checkSum,checkSum);
Call UART0_Write_;
AX0=SR0; !Byte 1
MathsEX_XORMATH1N(AX0,checkSum,checkSum);
Call UART0_Write_;
AR=%2;
SR=ASHIFT AR by 8 (lo);
AX0=SR1; !Byte 2
MathsEX_XORMATH1N(AX0,checkSum,checkSum);
Call UART0_Write_;
AX0=SR0; !Byte 3
MathsEX_XORMATH1N(AX0,checkSum,checkSum);
Call UART0_Write_;
AX0=0; !Code 0
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_allocate_Address(Old address, New address) *
* *
* Allocates a new address to the encoder *
* *
* Inputs : %0:encoders old address *
* %1:encoders new address *
* *
* Ouputs : none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_allocate_Address(%0,%1);
MathsEX_XORMATH2N(%0,0X55,checkSum);
MathsEX_XORMATH1N(%1,checkSum,checkSum);
MathsEX_XORMATH1N(0x55,checkSum,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X55; !Command
Call UART0_Write_;
AX0=%1; !new address
Call UART0_Write_;
AX0=0x55; !code zero
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_Read_Analogue_Value(encoder address, channell, data memory flag) *
* *
* Inputs : %0:encoder address *
* %1:channel that must be converted *
* *
* Ouputs : %2:memory address to hold the answer *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR, I0, M0 *
* *
***************************************************************************************}
.MACRO Stegmann_Read_Analogue_Value(%0,%1,%2);
MathsEX_XORMATH2N(%0,%1,checkSum);
MathsEX_XORMATH1N(0x44,checkSum,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X44; !Command
Call UART0_Write_;
AX0=%1; !Channel
Call UART0_Write_;
AX0=DM(checkSum);
Call UART0_Write_;
I0=^%2;
M0=1;
Call UART0_Read_; !Address
Call UART0_Read_; !Command
Call UART0_Read_; !Channel
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_read_Type_Lable(encoder address, data memory flag) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : %1:data memory address to hold the answer *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR, I0, M0 *
* *
***************************************************************************************}
.MACRO Stegmann_read_Type_Lable(%0,%1);
I0=^%1;
M0=1;
MathsEX_XORMATH2N(%0,0x52,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X52; !Command
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_reset(encoder address) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO stegmann_reset(%0);
MathsEX_XORMATH2N(%0,0X53,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X53; !Command
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
!No return
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_read_Serial_No_and_Prog_Ver(encoder address, data memory flag) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : %1:data memory flag to hold the answer *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR, I0, M0 *
* *
***************************************************************************************}
.MACRO Stegmann_read_Serial_No_and_Prog_Ver(%0,%1);
.local END_READ_SERIAL_NO;
I0=^%1;
M0=1;
MathsEX_XORMATH2N(%0,0X56,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X56; !Command
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_Output_Ref_Signal(encoder address) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : %1:none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_Output_Ref_Signal(%0);
MathsEX_XORMATH2N(%0,0X54,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X54; !Command
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
!No return
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_Clear_Counter(encoder address) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : %1:none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_Clear_Counter(%0);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X49; !Command
Call UART0_Write_;
AX0=0x55; !Zero Code
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_Increment_Counter(encoder address) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : %1:none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_Increment_Counter(%0);
MathsEX_XORMATH2N(%0,0x47,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X47; !Command
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_Read_Counter(encoder address, data memory flag) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : %1:data memory flag to hold the answer *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR, I0, M0 *
* *
***************************************************************************************}
.MACRO Stegmann_Read_Counter(%0,%1);
MathsEX_XORMATH2N(%0,0x46,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X46; !Command
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
I0=^%1;
M0=1;
Call UART0_Read_; !Address
Call UART0_Read_; !Command
Call UART0_Read_; !Byte 0
DM(I0,M0)=AX0;
Call UART0_Read_; !Byte 1
DM(I0,M0)=AX0;
Call UART0_Read_; !Byte 2
DM(I0,M0)=AX0;
Call UART0_Read_; !Check Sum
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_read_Data(encoder address, datafield, start address, number, code, *
* flag to the block of memory) *
* *
* Inputs : %0:encoder address *
* %1:datafield *
* %2:start address *
* %3:number of blocks *
* %4:access code *
* *
* Ouputs : %5:Data memory flag to the block of memory to hold the answer *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR, I0, M0 *
* *
***************************************************************************************}
.MACRO Stegmann_read_Data(%0,%1,%2,%3,%4,%5);
.local read_data_loop;
MathsEX_XORMATH2N(%0,0x4A,checkSum);
MathsEX_XORMATH1N(%1,checkSum,checkSum);
MathsEX_XORMATH1N(%2,checkSum,checkSum);
MathsEX_XORMATH1N(%3,checkSum,checkSum);
MathsEX_XORMATH1N(%4,checkSum,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X4A; ! read data
Call UART0_Write_;
AX0=%1; ! data field
Call UART0_Write_;
AX0=%2; ! start address
Call UART0_Write_;
AX0=%3; ! number
Call UART0_Write_;
AX0=%4; ! code
Call UART0_Write_;
AX0=DM(checkSum); ! check sum
Call UART0_Write_;
CNTR=%3;
I0=^%5;
M0=1;
do read_data_loop until ce;
Call UART0_Read_; !6
DM(I0,M0)=AX0;
read_data_loop:
nop;
Call UART0_Read_; !7
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_store_Data(encoder address, datafield, start address, number, code, *
* flag to the block of memory) *
* *
* *
* Inputs : %0:encoder address *
* %1:datafield *
* %2:start address *
* %3:number of blocks *
* %4:access code *
* *
* Ouputs : %5:Data memory flag to the block of memory to hold the answer *
* *
* Modified: AX0, AX1, AY0, AY1, AR, AF, SR0, SR1, SE, SI CNTR, MR0, MR1, MF, *
* MX1, MY0, MY1, I5, M5, L5 *
* *
***************************************************************************************}
.MACRO Stegmann_store_Data(%0,%1,%2,%3,%4,%5);
.local store_data_loop;
!not implemented
MathsEX_XORMATH2N(%0,0x4B,checkSum);
MathsEX_XORMATH1N(%1,checkSum,checkSum);
MathsEX_XORMATH1N(%2,checkSum,checkSum);
MathsEX_XORMATH1N(%3,checkSum,checkSum);
MathsEX_XORMATH1N(%4,checkSum,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X4B; !store data
Call UART0_Write_;
AX0=%1; ! data field
Call UART0_Write_;
AX0=%2; ! start address
Call UART0_Write_;
AX0=%3; ! number
Call UART0_Write_;
AX0=%4; ! code
Call UART0_Write_;
CNTR=%3;
I0=^%5;
M0=1;
do store_data_loop until ce;
AX0=DM(I0,M0);
MathsEX_XORMATH1N(AX0,checkSum,checkSum);
Call UART0_Write_; !6
store_data_loop:
nop;
AX0=DM(checkSum); ! check sum
Call UART0_Write_;
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_status_Data(encoder address, datafield, flag to the block of memory) *
* *
* Inputs : %0:encoder address *
* %1:datafield *
* *
* Ouputs : %2:Data memory flag to the block of memory to hold the answer *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_status_data(%0,%1,%2);
MathsEX_XORMATH2N(%0,0x4C,checkSum);
MathsEX_XORMATH1N(%1,checkSum,checkSum);
AX0=%0; ! Address
Call UART0_Write_;
AX0=0X4C; ! status data
Call UART0_Write_;
AX0=%1; ! data field
Call UART0_Write_;
AX0=DM(checkSum); ! check sum
Call UART0_Write_;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_create_Data(encoder address, datafield, status, code) *
* *
* Inputs : %0:encoder address *
* %1:datafield *
* %2:status *
* %3:access code *
* *
* Ouputs : none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_create_data(%0,%1,%2,%3);
MathsEX_XORMATH2N(%0,0x4D,checkSum);
MathsEX_XORMATH1N(%1,checkSum,checkSum);
MathsEX_XORMATH1N(%2,checkSum,checkSum);
MathsEX_XORMATH1N(%3,checkSum,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X4D; ! create data
Call UART0_Write_;
AX0=%1; ! data field
Call UART0_Write_;
AX0=%2; ! status
Call UART0_Write_;
AX0=%3; ! code
Call UART0_Write_;
AX0=DM(checkSum); ! check sum
Call UART0_Write_;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_available_memory(encoder address, flag to the block of memory) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : %1:Data memory flag to the block of memory to hold the answer *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR, I0, M0 *
* *
***************************************************************************************}
.MACRO Stegmann_available_memory(%0,%1);
MathsEX_XORMATH2N(%0,0x4E,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X4E; ! available data memory
Call UART0_Write_;
AX0=DM(checkSum); ! check sum
Call UART0_Write_;
I0=^%1;
M0=1;
Call UART0_Read_; !1 ! Address
Call UART0_Read_; !2 ! available data memory
Call UART0_Read_; !3 ! availsize
DM(I0,M0)=AX0;
Call UART0_Read_; !4 ! def_Nr
DM(I0,M0)=AX0;
Call UART0_Read_; !5 ! check sum
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_change_Acc_Code(encoder address, code_Nr, old code, new code) *
* *
* Inputs : %0:encoder address *
* %1:coder Nr *
* %2:old code *
* %3:new code *
* *
* Ouputs : none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_change_Acc_Code(%0,%1,%2,%3);
MathsEX_XORMATH2N(%0,0x4F,checkSum);
MathsEX_XORMATH1N(%1,checkSum,checkSum);
MathsEX_XORMATH1N(%2,checkSum,checkSum);
MathsEX_XORMATH1N(%3,checkSum,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X4F; ! change acc code
Call UART0_Write_;
AX0=%1; ! code_Nr
Call UART0_Write_;
AX0=%2; ! old code
Call UART0_Write_;
AX0=%3; ! new code
Call UART0_Write_;
AX0=DM(checkSum); ! check sum
Call UART0_Write_;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_encoder_Status(encoder address,flag to data memory to hold the answer)*
* *
* Inputs : %0:encoder address *
* *
* Ouputs : %1:flag to data memory to hold the answer *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_encounter_Status(%0,%1);
MathsEX_XORMATH2N(%0,0x50,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X50; ! encounter status
Call UART0_Write_;
AX0=DM(checkSum); ! check sum
Call UART0_Write_;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_read_Enc_Pos(encoder address) *
* *
* Inputs : %0:encoder address *
* *
* Ouputs : none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_read_Enc_Pos(%0);
MathsEX_XORMATH2N(%0,0x42,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0x42; !read position
Call UART0_Write_;
AX0=DM(checkSum); ! check sum
Call UART0_Write_;
Call UART0_Read_;
Call UART0_Read_;
Call UART0_Read_;
DM(return3)=AX0;
Call UART0_Read_;
DM(return4)=AX0;
Call UART0_Read_;
DM(return5)=AX0;
Call UART0_Read_;
DM(return6)=AX0;
Call UART0_Read_;
DM(return7)=AX0;
Stegmann_putTogetherDM(return3,return4,digitalPosMSB);
Stegmann_putTogetherDM(return5,return6,digitalPosLSB);
.ENDMACRO;
{***************************************************************************************
* *
* Type: Macro *
* *
* Call: Stegmann_Configure_Serial_Inferface(encoder address, Mode_485, code) *
* *
* Inputs : %0:encoder address *
* %1:Mode_485 information *
* %2:zero code *
* *
* Ouputs : none *
* *
* Modified: AX0, AY0, AY1, AR, MR0, MR1, SR0, SR1, CNTR *
* *
***************************************************************************************}
.MACRO Stegmann_Configure_Serial_Interface(%0,%1,%2);
MathsEX_XORMATH2N(%0,0X57,checkSum);
MathsEX_XORMATH0N(%1,checkSum,checkSum);
MathsEX_XORMATH0N(%2,checkSum,checkSum);
AX0=%0; !Address
Call UART0_Write_;
AX0=0X57; !Command
Call UART0_Write_;
AX0=DM(%1); !Mode_485
Call UART0_Write_;
AX0=DM(%2); !code zero
Call UART0_Write_;
AX0=DM(checkSum); !Check sum
Call UART0_Write_;
.ENDMACRO;
#endif
3.4 Stegmann.dsp
The following code contained in the file “Stegmann.dsp” defines the two routines mentioned in the
previous section.
The functions are declared as globally accessible to other applications.
.MODULE/RAM/SEG=USER_PM1 encoder_functions;
{***************************************************************************************
* *
* Library: Communication Library for the Stegmann Encoder *
* *
* File: Stegmann.dsp *
* *
* Description: Stegmann Code File *
* Purpose : Library Routines for Stegmann Encoder Operation *
* *
* Author : JB *
* Version : 1.0 *
* Date : January 2000 *
* Modification History: None *
* *
* Embedded Control Systems *
* Analog Devices Inc. *
***************************************************************************************}
#include <main.h>;
#include <admc401.h>;
#include <uart0.h>;
#include <adc401.h>;
#include <mathsEx.h>;
#include <trigono.h>;
#include <stegmann.h>;
{***************************************************************************************
* Calculate Configuration Register Contents from Parameters *
***************************************************************************************}
{None}
{***************************************************************************************
* Constants Defined in the Module *
***************************************************************************************}
{None}
{***************************************************************************************
* Routines Defined in this Module *
***************************************************************************************}
.ENTRY Stegmann_GetAnaloguePosition_;
.ENTRY Stegmann_Init_;
.ENTRY CheckSum;
{***************************************************************************************
* Global Variables Defined in this Module *
***************************************************************************************}
{***************************************************************************************
* Local Variables Defined in this Module *
***************************************************************************************}
Since the encoder may not be ideal and produce perfect Sin and Cos waveforms, it may be necessary to
correct offsets and amplitude differences. This application note does not provide the software for calculating
the offsets and amplitude errors, instead the user is expected to calibrate the system. One method is to create
a large data memory block and store successive position information. Using this data calculate the Max and
Min of each wave. Once the Max and Min are known then the offsets are calculated as follows:
SinMax − SinMin
→ offsetSin (1)
2
CosMax − CosMin
→ offsetCos (2)
2
Next, the amplitude compensation factor can be calculated. It is necessary to correct only one of the
waveforms. The sin waveform is corrected by multiplying the digitised sin value by the amplitude correction
factor. This factor is calculated using the following equation,
CosMax − CosMin
→ SinAmpComp (3)
SinMax − SinMin
For simplicity the offsets are set to zero and the amplitude compensation factor is set to one.
The task of the first routine is to get the initial position from the encoder. The encoder supplies this
information in four packets of 8 bits giving 32 bits of multi-turn position information. As explained in section
1.2 the first 6 bits are leading zero and the next 12 bits are multi-turn information. The next 9 bits plus 2 bits
of the cycle information make up the EIU count initial value.
In order to start the EIU block the EIUMAXCNT must be set. The value is 4*N-1 where N = number of lines
on the encoder.
Due to the way the encoder is designed, it is necessary to do an initial alignment of the analogue information
with the digital information. This is explained in detail in the encoder datasheet and a suggested algorithm is
also supplied, see Figure 9. This algorithm is implemented in this routine.
if (analogue angle < 180°) and (quadrature counter < 180°) Then no need to synchronise
if (analogue angle > 180°) and (quadrature counter > 180°) Then no need to synchronise
if (analogue angle >= 180°) and (quadrature counter < 180°) Then
diff = (analogue angle) - (quadrature counter)
if (diff < 180°) Then add 90° to quadrature counter // 0.. 179°, analogue angle preceding
else subtract 90° from quadrature counter // 0 .. -179°
else
diff = (quadrature counter)- (analogue angle)
if (diff < 180°) Then subtract 90° from quadrature counter // 0.. 179°, period counter preceding
else add 90° to quadrature counter // 0 .. -179°
{***************************************************************************************
* *
* Type: Routine *
* *
* Call: call Stegmann_Init_; *
* *
* Initializes the variables for the Stegmann Encoder Routines. *
* *
* Inputs : None *
* *
* Ouputs : AX0: MSB of initial position *
* AR: LSB of initial position *
* *
* Modified: AX0, AX1, AY0, AY1, AR, AF, SR0, SR1, SE, SI CNTR, MR0, MR1, MF, *
* MX1, MY0, MY1, I5, M5, L5 *
* *
***************************************************************************************}
Stegmann_Init_:
{
Set up EIU
In order of the EIU to start it my get an initial value for EIUMAXCNT
}
Setup_EIU:
AR=0X7FF; !512*4-1=2048-1 =>0x800-1=0X7FF see ADMC401 datasheet pages 36/37
DM(EIUMAXCNT)=AR;
Setup_Encoder:
{
The segment of code communicates with the encoder and retrieves the initial position
information. The information is shift first to find a value for the EIUCNT and then
to find the 5 bits of angle information.
}
Stegmann_read_Enc_Pos(addressOfEncoder);
AR=DM(digitalPosLSB);
AY0=0x3FFF;
AR=AR and AY0;
SR=ASHIFT AR by -3(lo);
DM(EIUCNT)=SR0;
SR=ASHIFT AR by -5(hi);
DM(return3)=SR0;
{
The analogue position is got and aligned with the digital position initial value.
}
CALL Stegmann_GetAnaloguePosition_;
The Stegmann alignment algorithm starts here.
AY0 = DM(return3);
AX0 = AR;
AR=AR XOR AY0;
AF=tstbit 15 of AR;
IF EQ jump end_of_alignment;
AF = tstbit 15 of AX0;
IF EQ jump Analog_less_oneeighty;
AX1=AY0;
AR=tstbit 15 of AX1;
IF NE jump Analog_less_oneeighty;
MY0=1;
AR=AX0;
!at this point you could handle complete cycle turn over.
SR=ASHIFT AR by -5 (lo);
DM(EIUCNT)=SR0;
jump end_of_alignment;
diff_less_one:
SR0=DM(EIUCNT);
SR=ASHIFT SR0 by 5 (lo);
AY1=B#100000;
AR=SR0-AY1;
!at this point you could handle complete cycle turn under.
SR=ASHIFT AR by -5 (lo);
DM(EIUCNT)=SR0;
jump end_of_alignment;
Analog_less_oneeighty:
MY0=1;
AR=AY0;
MR=AR * MY0 (uu);
MY0=AX0;
MR=MR-MX0*MY0(uu);
AF=tstbit 0 of MR1;
IF EQ jump diff_less_two;
SR0=DM(EIUCNT);
SR=ASHIFT SR0 by 5 (lo);
AY1=B#100000;
AR=SR0+AY1;
!at this point you could handle complete cycle turn over.
SR=ASHIFT AR by -5 (lo);
DM(EIUCNT)=SR0;
jump end_of_alignment;
diff_less_two:
SR0=DM(EIUCNT);
SR=ASHIFT SR0 by 5 (lo);
AY1=B#100000;
AR=SR0-AY1;
!at this point you could handle complete cycle turn under.
SR=ASHIFT AR by -5 (lo);
DM(EIUCNT)=SR0;
jump end_of_alignment;
Now that the two position words are aligned correctly all that’s left to do is combine them in the format
explained in section 1.2. The first 9 bits of the EIUCNT value are shifted to the left so as to occupy the MSB
of the first word. The first 7 bits of the analogue value are added to complete the first position word. The
remainder on the analogue value placed in the second position word, shifted into the MSB position.
end_of_alignment:
{
Now the aligned EIUCNT value is combined with the analogue value to get the overall position.
}
AR=AX0;
SR=ASHIFT AR by 7 (lo);
AX0=SR0;
AY1=SR1;
AR=DM(EIUCNT);
SR=ASHIFT AR by 14 (lo);
SR=ASHIFT SR1 by 7 (lo);
AR=SR0+AY1;
RTS;
SinZero:
AR=DM(cosSinZero);
AR=setbit 3 of AR;
DM(cosSinZero)=AR;
jump CosGet;
CosZero:
AR=DM(cosSinZero);
AR=setbit 2 of AR;
DM(cosSinZero)=AR;
AR=tstbit 1 of AR;
IF EQ jump minusNinety;
AR=0x3FFF;
jump end_sign;
minusNinety:
AR=0xBFFF;
jump end_sign;
This routine gets the analogue position information. It first gets the result from the ADC1, i.e. the digitised
Sine value and then ADC5, the Cos. In both cases it checks for a zero result.
{***************************************************************************************
* *
* Type: Routine *
* *
* Call: call Stegmann_GetAnaloguePosition_; *
* *
* Get the cycle position. IE the fine position in one of the 512 cycles. *
* *
* Inputs : None *
* *
* Ouputs : AX0: MSB of the cycle position *
* AR: LSB of the cycle position *
* *
* Modified: AX0, AX1, AY0, AY1, AR, AF, SR0, SR1, SE, SI CNTR, MR0, MR1, MF, *
* MX1, MY0, MY1, I5, M5, L5 *
* *
***************************************************************************************}
{
Get the sin value
Get the modulus making it an unsigned number => easier for ACRTAN calculation
Store status information
}
SinGet:
ADC_Read(ADC0, Offset_0to3); { Use ADC converter on ADCM401 }
AY0=DM(offSetSin); !Default set to zero
ENA AR_SAT;
{
To preserve all the information in a divide both the numerator and denominator are
shift to the left removing zero information and then the result is shifted by the relevant amount
}
section1:
SE=EXP SR0(HI);
SR = NORM SR0(lo);
DM(SINVALUE)=SR0;
AR=SE;
AR=-AR;
DM(SINVALUESH)=AR;
AX0= SR0;
{
Get the Cos value
Get the modulus making it an unsigned number => easier for ACRTAN calculation
Store status information
}
CosGet:
ADC_Read(ADC4, Offset_4to7); { Use ADC converter on ADCM401 }
AY0=DM(offSetCos); !Default set to zero
ENA AR_SAT;
AR=AR-AY0; !Correct for system offsets
DIS AR_SAT;
AR=ABS AR;
IF AV AR=pass 0x7FFF;
SR0=AR;
IF EQ jump cosZero;
IF POS jump section2;
AR= DM(cosSinZero);
AR=clrbit 0 of AR;
DM(cosSinZero)=AR;
section2:
SE=EXP SR0(HI);
SR = NORM SR0(lo);
DM(COSVALUE)=SR0;
AR=SE;
AR=-AR;
DM(COSVALUESH)=AR;
AY1=AX0;
AX0=SR0;
AY0=0;
AR=ax0-ay1;
IF GT jump do_divide;
SI=AY1;
SR=ASHIFT SI by -1 (HI);
AY1=SR1;
AR=DM(SINVALUESH);
AR=AR-1;
DM(SINVALUESH)=AR;
DM(SINVALUE)=AY1;
{
Do the divide
}
do_divide:
AF=PASS AY1;
ASTAT=0;
DIVQ AX0;DIVQ AX0;DIVQ AX0;DIVQ AX0;DIVQ AX0;DIVQ AX0;
DIVQ AX0;DIVQ AX0;DIVQ AX0;DIVQ AX0;DIVQ AX0;DIVQ AX0;
DIVQ AX0;DIVQ AX0;DIVQ AX0;DIVQ AX0;
{ Do ARCTAN }
Set_DAG_registers_for_trigonometric;
Atan(SR1,SR0);
AX0=DM(cosSinZero);
AF=tstbit 1 of AX0;
IF GT jump pos_sin;
AF=tstbit 0 of AX0;
IF GT jump n_sin_and_p_cos;
{ Since the value is between 0 and 90 it is necessary to correct for modulation earlier }
n_sin_and_n_cos:
AY1=0x1;
AR=AR-0X7FFF;
AR=AR-AY1;
jump end_sign;
n_sin_and_p_cos:
AR=-AR;
jump end_sign;
pos_sin:
AF=tstbit 0 of AX0;
IF GT jump end_sign;
AF=pass AR; !pos sin and neg cos
AX0=0x7FFF;
AR=AX0-AF;
IF AV jump carry_con;
AY0=1;
AR=AR+1;
jump end_sign;
carry_con:
AR=pass 0x7FFF;
end_sign:
nop;
rts;
.ENDMOD;
PWMSYNC is periodic a speed calculation can be carried out using the Stegmann_GetSpeed macro. This
is only possible if the Stegmann_Getpositon is calculated at a periodic rate. Another method is to use the
event timer to time consecutive Stegmann_GetPosition requests. The speed information is simply the
subtraction of last position (old_position) and current position (position). As already stated a further
scaling to the appropriate format is required.
Next, the general systems constants and PWM configuration constants (main.h – see the next section) are
included. Also included are the Library functions for the PWM, ADC, Uart0 and of course the application
specific routine - Stegmann.
{***************************************************************************************
* Include General System Parameters and Libraries *
***************************************************************************************}
#include <main.h>;
#include <adc401.h>;
#include <pwm401.h>;
#include <uart0.h>;
#include <stegmann.h>;
{***************************************************************************************
* Constants Defined in the Module *
***************************************************************************************}
{none}
{***************************************************************************************
* Global Routines Defined in this Module ( .Entry ) *
***************************************************************************************}
{ none }
{***************************************************************************************
* Global Variables Defined in this Module ( .Global ) *
***************************************************************************************}
{ None }
{***************************************************************************************
* Local Variables Defined in this Module *
***************************************************************************************}
.VAR/DM/RAM Old_position1;
.VAR/DM/RAM Old_position2;
.VAR/DM/RAM position1;
.VAR/DM/RAM position2;
.VAR/DM/RAM speed1;
.VAR/DM/RAM speed2;
The initialisation of PWM block, ADC and the Stegmann Encoder is executed and the interrupts are enabled.
The program then enters a loop, which just waits for interrupts.
{********************************************************************************************}
{ Start of program code }
{********************************************************************************************}
STARTUP:
UART0_Init;
PWM_Init(PWMSYNC_ISR, PWMTRIP_ISR);
ADC_Init;
RTS;
The interrupt service routine simply places the old position information into two data memory words and
gets the new position information. Then it calculates the speed, i.e. the difference between the new position
and the old position.
{********************************************************************************************}
{ PWM Interrupt Service Routine }
{********************************************************************************************}
PWMSYNC_ISR:
Copy_DM(position1,Old_position1);
Copy_DM(position2,Old_position2);
Stegmann_GetPosition(position1,position2);
Stegmann_GetSpeed(position1,position2,Old_position1,Old_position2,AX1,AX0);
RTI;
PWM-Trip service routine and initialisation of PWM block.
{********************************************************************************************}
{ PWM Trip Interrupt Service Routine }
{********************************************************************************************}
nop;
nop;
RTI;
.ENDMOD;
{********************************************************************************************}
{ Library: Stegmann Encoder functions }
{ file : stegmann.dsp }
{ Application Note: Interfacing the ADMC401 with a Stegmann Encoder }
Since there are now 10 bits of cycle information, plus 5 bits of internal cycle information, the first 15 bits
of digitalPosLSB must be stored and the 16th bit cleared, so line 132 of Stegmann.dsp changes to:
AY0=0x7FFF;
Next, due to the extra bit all shifting by 7 are now replaced by shifting by 6 as follows:
Line 221 of end_of_alignment in Stegmann.dsp
SR=ASHIFT AR by 6 (lo);
SR=ASHIFT AR by 6 (lo);
SR=ASHIFT SI by 6 (lo);
Figure 11: Quantization Error in Position over Electrical Cycle for 512 Line Sinusoidal Encoder
(Note that these values are specific to this test and may vary depending on temperature and other factors)
Then using an extremely high-resolution incremental encoder the position was calculated over one
complete electrical revolution using both the ADMC401 and the Stegmann SRS50 SINCOS® and the
high-resolution incremental encoder. The difference between the two results is plotted in Figure 12.
As can be seen the largest error is 0.6 electrical degrees. This error occurs at the 10th bit per electrical
revolution, or in the case of the 1024-line encoder position information at the 20th bit of complete cycle
information.
8
The encoder used was the SRS50, a single turn 1024-line SinCos® encoder.