Module that carry out the communication with the servomotor.
More information of TWIProtocol for the AVR_OpenServo_V3 branch here
More information of TWIProtocol for the AVR_OpenServo_V3-dev branch here
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;
Depending on the write state if it is a COMMAND or DATA different behavior:
switch (twi_data_state)
How it works:
We are sending data to our slave. If this data is lower than TWI_CMD_RESET(0x0800) what we are doing is to saving this register address in twi_address variable and changing the twi_date_state from COMMAND to DATA. When we enter again to this function the data that has arrived will be stored in the twi_address, the register we have sent previously and the twi_address will be increment one position. But if the data is greater than TWI_CMD_RESET this data will be stored in a buffer and in the main for will be executed asynchronously.
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;
// 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];
}
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;
TWI module
Module that carry out the communication with the servomotor.
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.
TWI States code
For one hand there are 6 general state codes for the TWI communication:
TWI.c
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.
If we read a normal register the read is done.
If an unused register is being read block the operation.
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?
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.
If we want to write to a read only register block write.
If we are writing to a read/write register complete the write
If writing to upper registers is disabled return and block the write.
If we are writing to a protected register complete the write if it is enabled.
Unused and redirect registers
twi_write_buffer() This function is enabled when TWI_CHECKED_ENABLED. twi_read_data() Simplify removing the TWI_CHECKED_ENABLED code
uint8_t twi_write_data(uint8_t data)
Simplify removing the TWI_CHECKED_ENABLED code
By default an ACK is returned
Depending on the write state if it is a COMMAND or DATA different behavior:
How it works:
If twi_data_state is TWI_DATA_STATE_COMMAND:
If twi_data_state is TWI_DATA_STATE_DATA:
twi_slave_init(uint8_t slave_address): Initialize USI(Universal Serial Interface) for TWI slave mode. Function used in main.c
Flush the buffers.
Set the TWI slave address in the TWAR.
Set defalut content in TWDR.
Initialize the TWI interrupt to wait for a new event.
twi_receive_byte()
twi_data_in_receive_buffer()
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.
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
TWCR (control register)
References