erlerobot / smart_motor

4 stars 2 forks source link

TWI module #22

Closed jlamperez closed 8 years ago

jlamperez commented 8 years ago

TWI module

Module that carry out the communication with the servomotor.

twi2 In twi.h some state codes and commands are defined to make the code more readable and also the the public functions. In twi.c the twi protocol is developed for a microcontroller.

TWI.h

OpenServo Commands

The OpenServo implements the following commands that can be sent by the master controller to enable certain operations of the servo.

#define TWI_CMD_RESET                   0x80        // Reset command
#define TWI_CMD_CHECKED_TXN             0x81        // Read/Write registers with simple checksum
#define TWI_CMD_PWM_ENABLE              0x82        // Enable PWM to motors
#define TWI_CMD_PWM_DISABLE             0x83        // Disable PWM to servo motors
#define TWI_CMD_WRITE_ENABLE            0x84        // Enable write of safe read/write registers
#define TWI_CMD_WRITE_DISABLE           0x85        // Disable write of safe read/write registers
#define TWI_CMD_REGISTERS_SAVE          0x86        // Save safe read/write registers fo EEPROM
#define TWI_CMD_REGISTERS_RESTORE       0x87        // Restore safe read/write registers from EEPROM
#define TWI_CMD_REGISTERS_DEFAULT       0x88        // Restore safe read/write registers to defaults
#define TWI_CMD_EEPROM_ERASE            0x89        // Erase the EEPROM.
#define TWI_CMD_VOLTAGE_READ            0x90        // Starts a ADC on the supply voltage channel
#define TWI_CMD_CURVE_MOTION_ENABLE     0x91        // Enable curve motion processing.
#define TWI_CMD_CURVE_MOTION_DISABLE    0x92        // Disable curve motion processing.
#define TWI_CMD_CURVE_MOTION_RESET      0x93        // Reset the curve motion buffer.
#define TWI_CMD_CURVE_MOTION_APPEND     0x94        // Append curve motion data.

TWI States code

For one hand there are 6 general state codes for the TWI communication:

twi_registers_read(uint8_t address) Read the byte from the specified register. This function handles the reading of special registers such as unused registers, redirect and redirected registers.

Mask the most significant bit of the address, in TWI addresses have 7 bits.

address &= 0x7F;

If we read a normal register the read is done.

    if (address <= MAX_WRITE_PROTECT_REGISTER)
    {
        // Yes. Complete the read.
        return registers_read_byte(address);
    }

If an unused register is being read block the operation.

    // Are we reading an unused register.
    if (address <= MAX_UNUSED_REGISTER)
    {
        // Block the read.
        return 0;
    }

If we are trying to read a redirect register.??? I don´t understand what a redirected register is. Why are two if (address <= MAX_REDIRECT_REGISTER) the second one is never used?

    // Are we reading a redirect register.
    if (address <= MAX_REDIRECT_REGISTER)
    {
        // Yes. Complete the read.
        return registers_read_byte(address - (MIN_REDIRECT_REGISTER - MIN_UNUSED_REGISTER));
    }
    // Are we reading a redirected register?
    if (address <= MAX_REDIRECTED_REGISTER)
    {
        // Adjust address to reference appropriate redirect register.
        address = MIN_REDIRECT_REGISTER  + (address - MIN_REDIRECTED_REGISTER);

        // Get the address from the redirect register.
        address = registers_read_byte(address - (MIN_REDIRECT_REGISTER - MIN_UNUSED_REGISTER));

        // Prevent infinite recursion.
        if (address <= MAX_REDIRECT_REGISTER)
        {
            // Recursively read redirected address.
            return twi_registers_read(address);
        }
    }
// All other reads are blocked.
    return 0;

twi_registers_write(uint8_t address, uint8_t data) Write non-write protected registers. This function handles the writing of special registers such as unused registers, redirect and redirected registers.

Mask the most significant bit of the address.

address &= 0x7F;

If we want to write to a read only register block write.

    if (address <= MAX_READ_ONLY_REGISTER)
    {
        // Yes. Block the write.
        return;
    }

If we are writing to a read/write register complete the write

    if (address <= MAX_READ_WRITE_REGISTER)
    {
        // Yes. Complete the write.
        registers_write_byte(address, data);

        return;
    }

If writing to upper registers is disabled return and block the write.

    if (registers_is_write_disabled())
    {
        // Yes. Block the write.
        return;
    }

If we are writing to a protected register complete the write if it is enabled.

    if (address <= MAX_WRITE_PROTECT_REGISTER)
    {
        // Yes. Complete the write if writes are enabled.
        registers_write_byte(address, data);

        return;
    }

Unused and redirect registers

    // Are we writing an unused register.
    if (address <= MAX_UNUSED_REGISTER)
    {
        // Yes. Block the write.
        return;
    }

    // Are we writing a redirect register.
    if (address <= MAX_REDIRECT_REGISTER)
    {
        // Yes. Complete the write.
        registers_write_byte(address - (MIN_REDIRECT_REGISTER - MIN_UNUSED_REGISTER), data);

        return;
    }

    // Are we writing a redirected register?
    if (address <= MAX_REDIRECTED_REGISTER)
    {
        // Adjust address to reference appropriate redirect register.
        address = MIN_REDIRECT_REGISTER  + (address - MIN_REDIRECTED_REGISTER);

        // Get the address from the redirect register.
        address = registers_read_byte(address - (MIN_REDIRECTED_REGISTER - MIN_UNUSED_REGISTER));

        // Prevent infinite recursion.
        if (address <= MAX_REDIRECT_REGISTER)
        {
            // Recursively write redirected address.
            twi_registers_write(address, data);

            return;
        }
    }

    // All other writes are blocked.
    return;

twi_write_buffer() This function is enabled when TWI_CHECKED_ENABLED. twi_read_data() Simplify removing the TWI_CHECKED_ENABLED code

    // By default read the data to be returned.
    uint8_t data = twi_registers_read(twi_address);

    // Increment the address.
    ++twi_address;
    return data;

uint8_t twi_write_data(uint8_t data)
Simplify removing the TWI_CHECKED_ENABLED code

This function is used with data = TWDR

By default an ACK is returned

uint8_t ack = TWI_ACK;

Depending on the write state if it is a COMMAND or DATA different behavior:

switch (twi_data_state)

How it works:

If twi_data_state is TWI_DATA_STATE_COMMAND:

     case TWI_DATA_STATE_COMMAND:
            // This is a byte.
            if (data < TWI_CMD_RESET)
            {
                // Capture the address.
                twi_address = data;

                // Update the write state.
                twi_data_state = TWI_DATA_STATE_DATA;
            }
            else
            {
                // Handle the command asynchronously.
                twi_rxhead = (twi_rxhead + 1) & TWI_RX_BUFFER_MASK;
                twi_rxbuf[twi_rxhead] = data;
            }

            break;

If twi_data_state is TWI_DATA_STATE_DATA:

        case TWI_DATA_STATE_DATA:

            // Write the data to the addressed register.
            twi_registers_write(twi_address, data);

            // Increment to the next address.
            ++twi_address;

            break;

twi_slave_init(uint8_t slave_address): Initialize USI(Universal Serial Interface) for TWI slave mode. Function used in main.c

Flush the buffers.

twi_rxtail = 0;
twi_rxhead = 0;

Set the TWI slave address in the TWAR.

TWAR = slave_address << 1;

Set defalut content in TWDR.

TWDR = 0xFF;

Initialize the TWI interrupt to wait for a new event.

    TWCR = (1<<TWEN) |                                  // Keep the TWI interface enabled.
           (1<<TWIE) |                                  // Keep the TWI interrupt enabled.
           (0<<TWSTA) |                                 // Don't generate start condition.
           (0<<TWSTO) |                                 // Don't generate stop condition.
           (1<<TWINT) |                                 // Clear the TWI interrupt.
           (1<<TWEA) |                                  // Acknowledge the data.
           (0<<TWWC);                                   //

twi_receive_byte()

// Returns a byte from the receive buffer. Waits if buffer is empty.
{
    // Wait for data in the buffer.
    while (twi_rxhead == twi_rxtail);

    // Calculate buffer index.
    twi_rxtail = (twi_rxtail + 1 ) & TWI_RX_BUFFER_MASK;

    // Return data from the buffer.
    return twi_rxbuf[twi_rxtail];
}

twi_data_in_receive_buffer()

// Check if there is data in the receive buffer.
{
    // Return 0 (FALSE) if the receive buffer is empty.
    return (twi_rxhead != twi_rxtail);
}

The name of the interruption has changed, now it is called TWI_vect instead of SIG_TWI(obsolete).

Used above important functions: twi_read_data() and twi_write_data(TWDR).

SIGNAL(SIG_TWI)

Implementation of the protocol. Depending of the State Register (TWSR) we have differente behaviours.

switch (TWSR)
case TWI_STX_ADR_ACK:
//Not implemented
case TWI_STX_DATA_ACK:

            // Read the checked/non-checked data.
            TWDR = twi_read_data();

            // Data byte will be transmitted and ACK should be received.
            TWCR = (1<<TWEN) |                              // Keep the TWI interface enabled.
                   (1<<TWIE) |                              // Keep the TWI interrupt enabled.
                   (0<<TWSTA) |                             // Don't generate start condition.
                   (0<<TWSTO) |                             // Don't generate stop condition.
                   (1<<TWINT) |                             // Clear the TWI interrupt.
                   (1<<TWEA) |                              // Acknowledge the data.
                   (0<<TWWC);                               //
            break;
// Data byte in TWDR has been transmitted; NOT ACK has been received.
case TWI_STX_DATA_NACK:
//Not implemented
        case TWI_STX_DATA_ACK_LAST_BYTE:

            // Switched to the not addressed slave mode; own SLA will be recognized.
            TWCR = (1<<TWEN) |                              // Keep the TWI interface enabled.
                   (1<<TWIE) |                              // Keep the TWI interrupt enabled.
                   (0<<TWSTA) |                             // Don't generate start condition.
                   (0<<TWSTO) |                             // Don't generate stop condition.
                   (1<<TWINT) |                             // Clear the TWI interrupt.
                   (1<<TWEA) |                              // Acknowledge the data.
                   (0<<TWWC);                               //
            break;
 // Own SLA+W has been received; ACK has been returned.
        case TWI_SRX_ADR_ACK:

            // Reset the data state.
            twi_data_state = TWI_DATA_STATE_COMMAND;

            // Data byte will be received and ACK will be returned.
            TWCR = (1<<TWEN) |                              // Keep the TWI interface enabled.
                   (1<<TWIE) |                              // Keep the TWI interrupt enabled.
                   (0<<TWSTA) |                             // Don't generate start condition.
                   (0<<TWSTO) |                             // Don't generate stop condition.
                   (1<<TWINT) |                             // Clear the TWI interrupt.
                   (1<<TWEA) |                              // Acknowledge the data.
                   (0<<TWWC);                               //

            break;
        // Previously addressed with own SLA+W; data has been received; ACK has been returned.
        case TWI_SRX_ADR_DATA_ACK:

            // Write the data.
            twi_write_data(TWDR);

            // Next data byte will be received and ACK will be returned.
            TWCR = (1<<TWEN) |                          // Keep the TWI interface enabled.
                   (1<<TWIE) |                          // Keep the TWI interrupt enabled.
                   (0<<TWSTA) |                         // Don't generate start condition.
                   (0<<TWSTO) |                         // Don't generate stop condition.
                   (1<<TWINT) |                         // Clear the TWI interrupt.
                   (1<<TWEA) |                          // Acknowledge the data.
                   (0<<TWWC);                           //

            break;
        // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned.
        case TWI_SRX_ADR_DATA_NACK:
//Not implemented
        case TWI_SRX_STOP_RESTART:

             // Switch to the not addressed slave mode; own SLA will be recognized.
             TWCR = (1<<TWEN) |                              // Keep the TWI interface enabled.
                    (1<<TWIE) |                              // Keep the TWI interrupt enabled.
                    (0<<TWSTA) |                             // Don't generate start condition.
                    (0<<TWSTO) |                             // Don't generate stop condition.
                    (1<<TWINT) |                             // Clear the TWI interrupt.
                    (1<<TWEA) |                              // Acknowledge the data.
                    (0<<TWWC);                               //

            break;
        // Bus error due to an illegal START or STOP condition.
        case TWI_BUS_ERROR:

            // Only the internal hardware is affected, no STOP condition is sent on the bus.
            // In all cases, the bus is released and TWSTO is cleared.
            TWCR = (1<<TWEN) |                              // Keep the TWI interface enabled.
                   (1<<TWIE) |                              // Keep the TWI interrupt enabled.
                   (0<<TWSTA) |                             // Don't generate start condition.
                   (1<<TWSTO) |                             // Don't generate stop condition.
                   (1<<TWINT) |                             // Clear the TWI interrupt.
                   (1<<TWEA) |                              // Acknowledge the data.
                   (0<<TWWC);                               //
            break;
        // No relevant state information available; TWINT="0".
        case TWI_NO_STATE:

            // No action required.
            break;
        default:

            // Reset the TWI interrupt to wait for a new event.
            TWCR = (1<<TWEN) |                                  // Keep the TWI interface enabled.
                   (1<<TWIE) |                                  // Keep the TWI interrupt enabled.
                   (0<<TWSTA) |                                 // Don't generate start condition.
                   (0<<TWSTO) |                                 // Don't generate stop condition.
                   (1<<TWINT) |                                 // Clear the TWI interrupt.
                   (1<<TWEA) |                                  // Acknowledge the data.
                   (0<<TWWC);                                   //
            break;

Summary (for me)

SIGNAL(TWI_vect) ----> twi_read_data() -----> twi_registers_read(twi_address) ---->twi_write_data(TWDR) ------>twi_registers_write(twi_address, data)

The COMMANDS are handled in main.c asynchronously inside the main for.

Notes about TWI(Two wire interface)

Popular serial peripheral interface bus

A TWI transmission consists of

Address packet

TWI Registers