SPI and AS5050 rotary encoder dev board, reading all zeros

14 Feb 2014

Hello. I must be doing something wrong. I am just reading zeros. If anyone can offer a suggestion I would appreciate it.

some notes:

  • I am using 4 wire mode, no interrupt for connection
  • reads and write to as5050 are msb first
  • looking at the diagram I think clock polarity is low, not sure about phase setting

as5050.cpp

#include <mbed.h>

/*!
*****************************************************************************
* Reads out chip data via SPI interface
*
* This function is used to read out cordic value from chips supporting SPI
* interface.
*****************************************************************************
*/
#define SPI_CMD_READ 0x8000
/*!< flag indicating read attempt when using SPI interface */
#define SPI_REG_DATA 0x7ffe
/*!< data register when using SPI */
#define SPI_REG_AGC 0x7ff0
/*!< agc register when using SPI */
#define SPI_REG_CLRERR 0x6700
/*!< clear error register when using SPI */

#define u8 unsigned char
#define u16 unsigned int
#define ubyte unsigned char
#define ushort unsigned short int
#define bit unsigned char

void spiReadData();
static u8 spiCalcEvenParity(ushort value);

SPI spi(p5,p6,p7);
DigitalOut cs(p8);
ushort spiTransfer(unsigned char *data, int num_bits);
Serial pc(USBTX, USBRX); // tx, rx ( the usb serial communication )

int main(void)
{
    int loop = 4;
    
    pc.printf("\r\n\r\n");
    spi.format(16,0);//16 bit data , low polarity, small delay between packets
    spi.frequency(100000);
    while(loop--)
    {
        spiReadData();   
    }
        
    while(1);
}


void spiReadData()
{
    u16 dat;
    ushort angle, agcreg;
    ubyte agc;
    ushort value;
    bit alarmHi, alarmLo;

    // 16-bit data buffer for SPI communication
    /* Send READ AGC command. Received data is thrown away: this data comes from the precedent
    command (unknown)*/
    dat = SPI_CMD_READ | SPI_REG_AGC;
    dat |= spiCalcEvenParity(dat);
    spiTransfer((u8*)&dat, sizeof(u16));

    /* Send READ ANGLE command. Received data is the AGC value, from the precedent command */
    dat = SPI_CMD_READ | SPI_REG_DATA;
    dat |= spiCalcEvenParity(dat);
    spiTransfer((u8*)&dat, sizeof(u16));
    agcreg = dat;


    /* Send NOP command. Received data is the ANGLE value, from the precedent command */
    dat = 0x0000;

    // NOP command.
    spiTransfer((u8*)&dat, sizeof(u16));
    angle = dat >> 2;
    if (((dat >> 1) & 0x1) || ((agcreg >> 1) & 0x1))
    {
        /* error flag set - need to reset it */
        dat = SPI_CMD_READ | SPI_REG_CLRERR;
        dat |= spiCalcEvenParity(dat);
        spiTransfer((u8*)&dat, sizeof(u16));
        pc.printf("error - resetting\r\n");
    }
    else
    {
        agc = (agcreg >> 2) & 0x3f;
        // AGC value (0..63)
        value = (dat >> 2) & 0x3fff;
        // Angle value (0..4095 for AS5055)
        angle = (value * 360) / 4095;
        // Angle value in degree (0..359.9°)
        alarmLo = (dat >> 14) & 0x1;
        alarmHi = (dat >> 15) & 0x1;
        
        pc.printf("AGC=0x%x\r\n",agc);
        pc.printf("value=0x%x\r\n",value);
        pc.printf("angle=0x%x\r\n",angle);
        pc.printf("alarmLo=0x%x\r\n",alarmLo);
        pc.printf("alarmHi=0x%x\r\n",alarmLo);
    }
    wait(.1);
}


/*!
*****************************************************************************
* Calculate even parity of a 16 bit unsigned integer
*
* This function is used by the SPI interface to calculate the even parity
* of the data which will be sent via SPI to the encoder.
*
* \param[in] value : 16 bit unsigned integer whose parity shall be calculated
*
* \return : Even parity
*
*****************************************************************************
*/
static u8 spiCalcEvenParity(ushort value)
{
    u8 cnt = 0;
    u8 i;

    for (i = 0; i < 16; i++)
    {
        if (value & 0x1)
        {
            cnt++;
        }
        value >>= 1;
    }
    return cnt & 0x1;
}

ushort spiTransfer(unsigned char *data, int num_bits)
{
    unsigned short int result =0;
    cs=0;
    result = spi.write(*data);
    cs = 1;    
    
    // store the result
    *data = result;
    
    return result;
    
 }


17 Feb 2014

Mark,

The basic problem is you are doing 8 but xfers , not 16 I think..

I have used this chip recently to get an angle,

Here is the algorithm

cs = 0 spiTransfer 0xFF spiTransfer 0xFF cs =1

throw away this stuff

cs = 0 spiTransfer 0x00 -=> get hi byte spiTransfer 0x00 => get low byte cs =1

angle is ((hibyte << 8) | lowbyte) >> 2

Google this

as5050-ab

Look for this

AS5050/AS5055-AB User Manual - ams

and download it.. It has info in it also..

From the manual

"In this configuration with n x encoders, the sequence will be processed as follow: - MCU sets SS/ = 0 - MCU shifts n x 16-bit (e.g. READ command FFFFh) through the chain - MCU sets SS/=1 At that point all the n x encoders have received the READ command FFFFh. - MCU sets SS/=0 - MCU shifts n x 16-bit (e.g. NOP command 0000h) - MCU sets SS/=1 At that point the n x 16-bit received on MISO are the n x angle values."

http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCYQFjAA&url=http%3A%2F%2Fwww.ams.com%2Feng%2Fcontent%2Fdownload%2F207989%2F887199&ei=1i8CU6-2BOHu2wWA3oG4Ag&usg=AFQjCNE67PB6_OeezycidFajlhDV1v9ScA&bvm=bv.61535280,d.b2I&cad=rja

HTH

SteveH

shoutchen@gmail.com

20 Feb 2014

Steve, Thanks for that info. It seems I am closer to making this work. I read all zeros until I pull the magnet knob out of the reference board, then I get all 0xFFs. I wonder why 0xFFFF seems to work; the register address is 0x3FFF and ORing the high bit 0x8000 gives me 0xBFFF.

Currently I am trying to get a simple sanity test to work: reading the AGC register and verifying its reset value matches the manual (0x20). I am spending about an hour a day on this.

Thanks, mark