I am working on the MD23 driver system since 2 weeks, now. I use a Polybot Board ATMEGA644 (from John Seng) to communicate with the MD23 through the I2C bus.
I can drive the motor , read the version and the current, I can also change the motor timeout, but I cannot read the encoder register
here is the library I use to:
- Code: Select all
/******************************************************************************
* i2c.c
*
* Provides I2C functions for the Atmel ATmega644 for use with PolyBot
*
* Steven Cary
* sjcary@gmail.com
*
* version 0.9 - 6/15/2007
*
*
*****************************************************************************/
#include <avr/io.h>
#include "globals.h"
#include "I2C.h"
#include "util/twi.h"
#include "delays.h"
#define unsigned char u08;
u08 i2c_err = 0;
/* internal function declarations */
void go_I2C(u08 w_byte);
u08 get_stat_I2C(void);
void nack_I2C(void);
void ack_I2C(void);
/* Generate an I2C start condition */
void start_I2C(void)
{
TWCR = ((1 << TWSTA) | (1 << TWINT) | (1 << TWEN));
}
/* Generate an I2C Stop Condition */
void stop_I2C(void)
{
TWCR = ((1 << TWINT) | (1 << TWEN) | (1 << TWSTO));
}
/* Start master reception and generate an I2C NACK after next received byte*/
void nack_I2C(void)
{
TWCR = ((1 << TWINT) | (1 << TWEN));
}
/* Start master reception and generate I2C ACK after next received byte */
void ack_I2C(void)
{
TWCR = ((1 << TWINT) | (1 << TWEN) || (1 << TWEA));
}
/* returns TWINT. I2C hardware is busy if TWINT is cleared */
u08 busy_I2C(void)
{
u08 loop_counter = 0;
while (!(TWCR & (1 << TWINT))) { //loop while the I2C bus is busy
loop_counter++;
if (loop_counter > 250) //if 250 iterations have passed, return a timeout
return 1;
}
return 0;
}
/* Checks if the stop bit has been sent */
u08 stop_in_progress(void)
{
return (TWCR & (1 << TWSTO));
}
u08 f_addr_I2C(u08 sl_addr)
{
start_I2C(); /* Send start bit */
go_I2C(sl_addr & 0xFE); /* Pass the slave address with read bit low */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MT_SLA_ACK) { //check for ACK on the slave address
return get_stat_I2C();
}
stop_I2C();
while (stop_in_progress()); /* Do not exit until stop has been sent */
return 0;
}
/******************************************************************************
* Master Mode:
* For use with interrupts disabled.
* A complete transmission of w_byte to the I2C slave with address sl_addr.
* Returns: 0 - success
* error byte - on error. See header for errors.
*
*****************************************************************************/
u08 putc_I2C(u08 w_byte, u08 sl_addr)
{
start_I2C(); /* Send start bit */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_START) { //check that the start bit was okay
return get_stat_I2C();
}
go_I2C(sl_addr & 0xFE); /* Pass the slave address with read bit low */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MT_SLA_ACK) { //check for ACK on the slave address
return get_stat_I2C();
}
go_I2C(w_byte); /* Send the data byte to write */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MT_DATA_ACK) { //check for ACK on the data sent
return get_stat_I2C();
}
stop_I2C();
while (stop_in_progress()); /* Do not exit until stop has been sent */
return 0;
}
/* places the passed in byte in to TWDR, then initiates I2C byte transfer
* This function should not be used externally*/
void go_I2C(u08 w_byte)
{
while (busy_I2C()); /* Loop until transmitter not busy */
TWDR = w_byte; /* Place the byte in the transmission reg */
TWCR = W_DATA_I2C_INTOFF; /* Write to the Control Reg to initiate write */
}
/* Returns the status bits */
u08 get_stat_I2C(void)
{
return (TWSR & 0xF8);
}
/******************************************************************************
* Master Mode:
* Writes len character of string pointed to by str to the I2C bus
* Returns: 0 - All characters successfully written
* nonzero - Error occurred, status returned
*****************************************************************************/
u08 putns_I2C(u08 *str, u08 len, u08 sl_addr)
{
u08 stat;
start_I2C(); /* Send start bit */
if (busy_I2C())
return 0xf; //timeout
if ((stat = get_stat_I2C()) != TW_START) { //check if start bit was sent
return (get_stat_I2C());
}
go_I2C(sl_addr); /* Pass the slave address and read bit = 0 */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MT_SLA_ACK) { //check for ACK on the slave address
return (get_stat_I2C());
}
/* Send the command string */
while (len-- > 0) {
go_I2C(*str++);
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MT_DATA_ACK) { //check for ACK on the data
return (get_stat_I2C());
}
}
if (busy_I2C())
return 0xf; //timeout
stop_I2C();
while (stop_in_progress()); /* Do not exit until stop has been sent */
return 0;
}
/******************************************************************************
* Read a single character from register reg from the I2C bus device at sl_addr
*
* Returns: The byte read from the bus. The calling function can check the
error register to make sure the transmission completed successfully.
This function is much the same as read_I2C but a specific register
within the I2C device is specified to be read from. This function
can be used on devices that implement the common EEPROM interface.
*****************************************************************************/
u08 get_I2C(u08 sl_addr, u08 reg)
{
u08 data = 0;
start_I2C(); //send a start bit
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_START) { //check for start bit error
return (i2c_err = get_stat_I2C());
}
go_I2C(sl_addr & 0xFE); /* Pass the slave address and read bit set to 0 */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MT_SLA_ACK) { //check for slave address ACK error
return (i2c_err = get_stat_I2C());
}
go_I2C(reg); //send the register to read from
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MT_DATA_ACK) { //check for data ACK error
return (i2c_err = get_stat_I2C());
}
if (busy_I2C())
return 0xf; //timeout
/*repeated Start, then read from the device */
start_I2C(); //send another start
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_REP_START) { //check for repeated start error
return (i2c_err = get_stat_I2C());
}
go_I2C(sl_addr | 1); /* Pass the slave address, read bit = 1 */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MR_SLA_ACK) { //check for slave address ACK error
return (i2c_err = get_stat_I2C());
}
/* receive the first byte and reply with NACK to signal transmission end */
nack_I2C();
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MR_DATA_NACK) { //check for NACK error
return (i2c_err = get_stat_I2C());
}
i2c_err = 0; /* No errors */
data = TWDR;
stop_I2C();
while (stop_in_progress()); /* Do not exit until stop has been sent */
return data; /* Return the received data */
}
/******************************************************************************
* Read a single character from the I2C bus device at sl_addr
*
* Returns: The byte read from the bus. The calling function should check the
error register to make sure the transmission completed successfully.
*****************************************************************************/
u08 read_I2C(u08 sl_addr) /* TODO Ensure this function works properly */
{
u08 data;
start_I2C(); /* Send start bit */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_START) { /* Check for errors */
i2c_err = get_stat_I2C();
return i2c_err;
}
go_I2C(sl_addr | 1); /* Pass the slave address and read bit set to 1 */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MR_SLA_ACK) { /* Check for errors */
i2c_err = get_stat_I2C();
return i2c_err;
}
/* Set transmitter to send NACK after next byte received */
nack_I2C();
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MR_DATA_NACK) {
i2c_err = get_stat_I2C();
return i2c_err;
}
/* data is ready in TWDR */
data = TWDR;
stop_I2C();
while (stop_in_progress()); /* Do not exit until stop has been sent */
i2c_err = 0; /* No errors */
return data; /* Return the contents of the data register */
}
/* Attempt to read n bytes from the I2C bus */
/* TODO Ensure this function works properly */
u08 readn_I2C(u08 sl_addr, u08 num_bytes, u08 * data)
{
int i;
start_I2C(); /* Send start bit */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_START) { //check for error on start bit
i2c_err = get_stat_I2C();
return 0;
}
go_I2C(sl_addr | 1); /* Pass the slave address and read bit set to 1 */
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MR_SLA_ACK) { //check for slave address ACK error
i2c_err = get_stat_I2C();
return 0;
}
/* add error checking here */
for (i = 0; i < num_bytes - 1; i++) {
ack_I2C();
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MR_DATA_ACK) { //check for data reception ACK error
i2c_err = get_stat_I2C();
return 0;
}
data[i] = TWDR;
}
/* Set transmitter to send NACK after next byte received */
nack_I2C();
if (busy_I2C())
return 0xf; //timeout
if (get_stat_I2C() != TW_MR_DATA_NACK) { //check for NACK error
i2c_err = get_stat_I2C();
return 0;
}
/* data is ready in TWDR */
data[i] = TWDR;
stop_I2C();
while (stop_in_progress()); /* Do not exit until stop has been sent */
i2c_err = 0; /* No errors */
return 0;
}
#define SDA_LOW PORTC = PINC & 0xFD
#define SDA_HIGH PORTC = PINC | 0x02
#define SCL_LOW PORTC = PINC & 0xFE
#define SCL_HIGH PORTC = PINC | 0x01
#define I2C_INPUT DDRC &= 0xFC
#define I2C_OUTPUT DDRC |= 0x3
/* Read 1 byte with a slave that does clock stretching */
u08 read_I2C_cs(u08 sl_addr)
{
u08 i;
u08 bit;
u08 data;
SDA_HIGH;
SCL_HIGH;
//turn off the I2C hardware
TWCR = 0;
delay_us(100);
//make sure that both pins are high
while ((PINC & 3) != 3) {}
I2C_OUTPUT; //set both pins to be outputs
//send start bit
SDA_LOW;
delay_us(100);
SCL_LOW;
delay_us(100);
//send the slave address
sl_addr = sl_addr | 1;
for (i=0;i<8;i++) {
bit = sl_addr & 0x80;
if (bit != 0) { //send a 1
SDA_HIGH; //set SDA high
delay_us(5);
SCL_HIGH; //toggle SCL
delay_us(5);
SCL_LOW;
} else { //send a 0
SDA_LOW; //set SDA low
delay_us(5);
SCL_HIGH; //toggle SCL
delay_us(5);
SCL_LOW;
}
sl_addr = sl_addr << 1;
}
DDRC &= 0xFD; //set SDA to be input
delay_us(5);
SCL_HIGH; //clock in the ACK from the slave
delay_us(5);
SCL_LOW;
delay_us(5);
data = 0;
for (i=0;i<8;i++) {
data = data << 1;
DDRC &= 0xFE; //set the SCL to be input and release the SCL
delay_us(2);
while ((PINC & 1) == 0) {} //loop while the clock is low
data = data | ((PINC & 2) >> 1);
delay_us(5);
DDRC |= 1; //set the SCL to be an output
SCL_LOW;
delay_us(5);
}
SDA_HIGH; //send the NACK
DDRC |= 0x2; //set SDA to be output
delay_us(5);
DDRC &= 0xFE; //set the SCL to be input and release the SCL
delay_us(2);
while ((PINC & 1) == 0) {} //loop while the clock is low
delay_us(5);
DDRC |= 1; //set the SCL to be an output
SCL_LOW;
delay_us(5);
//send stop
SDA_LOW;
DDRC |= 2;
delay_us(5);
SCL_HIGH;
delay_us(5);
SDA_HIGH;
delay_us(5);
open_I2C(I2C_ONE_HUNDRED_KHZ);
I2C_INPUT; //set pins to input
return data;
}
/* Read 2 bytes with a slave that does clock stretching */
u16 readn_I2C_cs(u08 sl_addr)
{
u08 i,j;
u08 bit;
u08 data;
u08 result[2];
SDA_HIGH;
SCL_HIGH;
//turn off the I2C hardware
TWCR = 0;
delay_us(5);
//make sure that both pins are high
while ((PINC & 3) != 3) {}
I2C_OUTPUT; //set both pins to be outputs
//send start bit
SDA_LOW;
delay_us(5);
SCL_LOW;
delay_us(5);
//send the slave address
sl_addr = sl_addr | 1;
for (i=0;i<8;i++) {
bit = sl_addr & 0x80;
if (bit != 0) { //send a 1
SDA_HIGH; //set SDA high
delay_us(5);
SCL_HIGH; //toggle SCL
delay_us(5);
SCL_LOW;
} else { //send a 0
SDA_LOW; //set SDA low
delay_us(5);
SCL_HIGH; //toggle SCL
delay_us(5);
SCL_LOW;
}
sl_addr = sl_addr << 1;
}
DDRC &= 0xFD; //set SDA to be input
delay_us(5);
SCL_HIGH; //clock in the ACK from the slave
delay_us(5);
SCL_LOW;
delay_us(5);
for (j=0;j<2;j++) {
DDRC &= 0xFD; //set SDA to be input
data = 0;
for (i=0;i<8;i++) {
data = data << 1;
DDRC &= 0xFE; //set the SCL to be input and release the SCL
delay_us(2);
while ((PINC & 1) == 0) {} //loop while the clock is low
data = data | ((PINC & 2) >> 1);
delay_us(5);
DDRC |= 1; //set the SCL to be an output
SCL_LOW;
delay_us(5);
}
result[j] = data;
if (j == 0)
SDA_LOW; //send the ACK
else
SDA_HIGH; //send the NACK
DDRC |= 0x2; //set SDA to be output
delay_us(5);
DDRC &= 0xFE; //set the SCL to be input and release the SCL
delay_us(2);
while ((PINC & 1) == 0) {} //loop while the clock is low
delay_us(5);
DDRC |= 1; //set the SCL to be an output
SCL_LOW;
delay_us(5);
}
//send stop
SDA_LOW;
DDRC |= 2;
delay_us(5);
SCL_HIGH;
delay_us(5);
SDA_HIGH;
delay_us(5);
open_I2C(I2C_ONE_HUNDRED_KHZ);
I2C_INPUT; //set pins to input
return (result[0] << 8) + result[1];
}
/******************************************************************************
* Configures the I2C bus in the desired mode. See definitions in header.
*****************************************************************************/
void open_I2C(unsigned char mode)
{
switch (mode) {
case I2C_ONE_HUNDRED_KHZ:
/* Set the Config register */
TWBR = TWBR_ONE_HUNDRED_KHZ; /* Set the Bit rate reg */
TWSR = TWSR & 0xF8; /* clear bottom three bits */
TWSR = TWSR | (0x03 & TWPS_ONE_HUNDRED_KHZ); /* Set the prescaler */
break;
case I2C_FOUR_HUNDRED_KHZ:
/* Set the Config register */
TWBR = TWBR_FOUR_HUNDRED_KHZ; /* Set the Bit rate reg */
TWSR = TWSR & 0xF8; /* clear bottom three bits */
TWSR = TWSR | (0x03 & TWPS_FOUR_HUNDRED_KHZ); /* Set the prescaler */
break;
case I2C_FORTY_KHZ:
/* Set the Config register */
TWBR = TWBR_FORTY_KHZ; /* Set the Bit rate reg */
TWSR = TWSR & 0xF8; /* clear bottom three bits */
TWSR = TWSR | (0x03 & TWPS_FORTY_KHZ); /* Set the prescaler */
break;
}
TWCR |= ((1 << TWEN)); /* Enable the I2C hardware */
delay_ms(1);
}
and the .h
- Code: Select all
/******************************************************************************
* i2c.h
*
* Provides I2C functions for the Atmel ATmega644 with I2C interrupts disabled
*
* Steven Cary
* sjcary@gmail.com
*
* version 0.9 - 6/15/2007
*
*****************************************************************************/
#ifndef I2C_H
#define I2C_H
#define W_DATA_I2C_INTOFF 0xC4 /* Set TWINT, TWEA, TWEN.
Clear TWWSTA, TWSTO, TWIE, TWWC */
#include "globals.h"
#if(F_CPU == 20000000UL)
/* SCL Freq = (CPU Clock freq / (16+2(TWBR) * 4^TWPS))
Set for 400khz with 20MHz clock: TWBR = 17, TWPS = 0
Set for 100Khz with 20MHz clock: TWBR = 92, TWPS = 0 */
#define TWBR_FOUR_HUNDRED_KHZ 17
#define TWPS_FOUR_HUNDRED_KHZ 0
#define TWBR_ONE_HUNDRED_KHZ 92
#define TWPS_ONE_HUNDRED_KHZ 0
#define TWBR_FORTY_KHZ 4
#define TWPS_FORTY_KHZ 3
#define I2C_ONE_HUNDRED_KHZ 0
#define I2C_FOUR_HUNDRED_KHZ 1
#define I2C_FORTY_KHZ 2
#endif /* F_CPU = 20000000 */
/**** Uncomment to enable error checking after each I2C action */
#define I2C_ERR_CHECK
extern u08 i2c_err;
/* Loops until I2C bus is idle */
u08 busy_I2C(void);
/* u08 f_addr_I2C(u08 sl_addr)
/******************************************************************************
* Master Mode:
* For use with interrupts disabled.
* A complete transmission of w_byte to the I2C slave with address sl_addr.
* Returns: 0 - success
* error byte - on error. See header for errors.
*
*****************************************************************************/
u08 putc_I2C(u08 w_byte, u08 sl_addr);
/******************************************************************************
* Master Mode:
* Writes len character of string pointed to by str to the I2C bus
* Returns: 0 - All characters successfully written
* nonzero - Error occurred, status returned
*****************************************************************************/
u08 putns_I2C(u08 *str, u08 len, u08 sl_addr);
/******************************************************************************
* Read a single character from register reg from the I2C bus device at sl_addr
*
* Returns: The byte read from the bus. The calling function can check the
error register to make sure the transmission completed successfully.
This function is much the same as read_I2C but a specific register
within the I2C device is specified to be read from. This function
can be used on devices that implement the common EEPROM interface.
*****************************************************************************/
u08 get_I2C(u08 sl_addr, u08 reg);
/******************************************************************************
* Read a single character from the I2C bus device at sl_addr
*
* Returns: The byte read from the bus. The calling function should check the
error register to make sure the transmission completed successfully.
*****************************************************************************/
u08 read_I2C(u08 sl_addr);
/*****************************************************************************
* Attempt to read n bytes from the I2C bus
****************************************************************************/
u08 readn_I2C(u08 sl_addr, u08 num_bytes, u08 *data);
/******************************************************************************
* Configures the I2C bus in the desired mode. See definitions above.
*****************************************************************************/
void open_I2C(unsigned char mode);
/******************************************************************************
* Generates an I2C start condition
*****************************************************************************/
void start_I2C(void);
/******************************************************************************
* Generates an I2C stop condition
*****************************************************************************/
void stop_I2C(void);
u08 read_I2C_cs(u08 sl_addr);
u16 readn_I2C_cs(u08 sl_addr);
#endif /* I2C_H */
and the main code :
- Code: Select all
#include "globals.h"
#include "I2C.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
int main(void) {
initialize();
u08 enco1,enco2,enco3,enco4;
unsigned char stat;
u16 comp_range = 0;
u08 addr;
char cmd[] = {16, 0x20 };
char cmd1[] = {16, 0x32 };
char cmd2[] = {0, 100 };
char cmd3[] = {1, 100 };
unsigned char data[2];
open_I2C(0); // start I2C at 100KHz
putns_I2C(cmd , 2 , 0xB0); // reset encoder register
putc_I2C(0x20 , 0xB0);
putns_I2C(cmd1 , 2 , 0xB0);//disable timeout motor
putc_I2C(0x32 , 0xB0);
putns_I2C(cmd2 , 2 , 0xB0);// motor 1
putns_I2C(cmd3 , 2 , 0xB0);//motor2
while(1) {
enco1=get_I2C(0xB0,2);
enco2=get_I2C(0xB0,3);
enco3=get_I2C(0xB0,4);
enco4=get_I2C(0xB0,5);
print_string("Encoder :");
print_int(enco1);
print_int(enco1);
print_int(enco1);
print_int(enco1);
delay_ms(5000);
clear_screen();
}
return 0;
}
Could you please tell me why It does not work??
Best Regards
