Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C writing and writing_bytes cannot seem to write more than 2 bytes of data at a time #27

Open
davec2 opened this issue Feb 1, 2013 · 6 comments

Comments

@davec2
Copy link

davec2 commented Feb 1, 2013

I think this is an issue. It caught me by surprise, but sometimes that doesn't take much. I hope this is the correct place for this topic.

I'm running Rasparian wheezy with kernel 3.2.27 on a Rev 2 RPi, and am attempting to write more than 2 bytes to an I2C slave device. I did the following in python3:
import quick2wire.i2c as i2c
address = 0x01
with i2c.I2CMaster() as bus:
bus.transaction(i2c.writing_bytes(address, 0x01,0x02))

I get the following error:

File "/home/pi/quick2wire-python-api-master/quick2wire/i2c.py", line 74, in transaction
ioctl(self.fd, I2C_RDWR, ioctl_arg)

IOError: [Errno 5] Input/output error

I have an I2C sniffer on the SDA and SCL pins - I see the two bytes on the line, but I still get this error.

Further, if I try to send more than 2 bytes, I only ever see just the first two bytes on the I2C bus, and I get the same error as above.

I was under the impression that I could send any length of byte values using the i2c.py methods?

@npryce
Copy link
Contributor

npryce commented Feb 3, 2013

I usually see "IOError: [Errno 5] Input/output error" when I'm trying to send to a non-existent address.

Have you successfully sent single bytes?

@davec2
Copy link
Author

davec2 commented Feb 11, 2013

Hello Nat,

Sorry for the delay in responding!

I have indeed been able to successfully send single bytes.

Dave

Date: Sun, 3 Feb 2013 15:02:11 -0800
From: [email protected]
To: [email protected]
CC: [email protected]
Subject: Re: [quick2wire-python-api] I2C writing and writing_bytes cannot seem to write more than 2 bytes of data at a time (#27)

I usually see "IOError: [Errno 5] Input/output error" when I'm trying to send to a non-existent address.

Have you successfully sent single bytes?

          —

          Reply to this email directly or view it on GitHub.

@romilly
Copy link
Owner

romilly commented Feb 17, 2013

I've just done a test writing and reading multiple bytes to a 24LC512 eeprom, and everything works as expected.

Dave, what is the device you are writing to?

@davec2
Copy link
Author

davec2 commented Feb 26, 2013

Sorry for the delay in responding!

That's curious - I am writing to an Atmel Atmega168, which has been programmed to send and receive multiple bytes via I2C. I've tested the Atmega168 and found it to work with other devices. But when I tried to write to it with the quick2wire api, I couldn't get more than 2 bytes to go across at a time. I confirmed this with an I2C protocl analyzer (a gabotronics xprotolab). I wonder what is different? I'm tempted to send one of these to you to see if you can get it to work.

The code I have running on the Atmega168 is on my other computer -I'll get it out to you as an attachment if you want.

Cheers,
Dave

Date: Sun, 17 Feb 2013 07:25:06 -0800
From: [email protected]
To: [email protected]
CC: [email protected]
Subject: Re: [quick2wire-python-api] I2C writing and writing_bytes cannot seem to write more than 2 bytes of data at a time (#27)

I've just done a test writing and reading multiple bytes to a 24LC512 eeprom, and everything works as expected.

Dave, what is the device you are writing to?

          —

          Reply to this email directly or view it on GitHub.

@romilly
Copy link
Owner

romilly commented Feb 26, 2013

Thanks for the extra background. A copy of the 168 code would be useful.

As it happens, some of the code I need to work on today involves sending
I2C data from a Pi to an arduino clone. So far I've been sending single
bytes but today I'll try more than one. I'll capture bus activity with the
Open Workbench Logic Sniffer and report results.

I'm sorry you're experiencing problems, but this kind of user feedback is
really valuable. Either our code is doing something wrong, (in which case
we need to find and fix it), or you are (in which case our documentation is
defective, and we need to fix that).

I've copied this to our user group, just in case someone else has
similar problems or has an idea of what's going wrong.

On 26 February 2013 00:53, davec2 [email protected] wrote:

Sorry for the delay in responding!

That's curious - I am writing to an Atmel Atmega168, which has been
programmed to send and receive multiple bytes via I2C. I've tested the
Atmega168 and found it to work with other devices. But when I tried to
write to it with the quick2wire api, I couldn't get more than 2 bytes to go
across at a time. I confirmed this with an I2C protocl analyzer (a
gabotronics xprotolab). I wonder what is different? I'm tempted to send one
of these to you to see if you can get it to work.

The code I have running on the Atmega168 is on my other computer -I'll get
it out to you as an attachment if you want.

Cheers,
Dave

Date: Sun, 17 Feb 2013 07:25:06 -0800
From: [email protected]
To: [email protected]
CC: [email protected]
Subject: Re: [quick2wire-python-api] I2C writing and writing_bytes cannot
seem to write more than 2 bytes of data at a time (#27)

I've just done a test writing and reading multiple bytes to a 24LC512
eeprom, and everything works as expected.

Dave, what is the device you are writing to?

Reply to this email directly or view it on GitHub.


Reply to this email directly or view it on GitHubhttps://github.com//issues/27#issuecomment-14086245
.

http://quick2wire.com safe, simple connection to your Raspberry Pi

@davec2
Copy link
Author

davec2 commented Mar 4, 2013

Sorry for taking this long to get back to you - it has been a rather strange few days.

So anyway, I have attached the code which I programmed onto the Atmel AVR ATMEGA168 processor. The code reflects the send-one-byte-at-a-time nature that I discovered when running the python module on the RPi. However, I direct your attention to the function handleI2C() - in it is a case statement which first tests for TW_STATUS equaling 0x60; the condition on r_index >= BUFLEN_RECV should allow the Atmega to return a NACK; this should then allow the I2C master to send more bytes. For whatever reason, I could not get this to work on the RPi with the quick2wire module, most likely due to my feeble programming skills more than anything else. I would be most grateful if you could see if you can get it to work.

I've written this code so that one byte per I2C write can be received. In order to return it to it original state, where it will try to receive 5 bytes at a go, do the following:
uncomment line 178
comment out lines 288,289, and291
I compiled this on AVR Studio 4 running on a Windows 7 machine. The dependencies are the usual set of gcc libraries from AVRStudio. I do remember starting out with some I2C slave code obtained from the internet (perhaps AVR Freaks' forum??), but I cannot now recall from where I got it.

I hope this helps. I am quite interested in hearing how well it works (or doesn't)!

Dave
Date: Mon, 25 Feb 2013 23:10:14 -0800
From: [email protected]
To: [email protected]
CC: [email protected]
Subject: Re: [quick2wire-python-api] I2C writing and writing_bytes cannot seem to write more than 2 bytes of data at a time (#27)

Thanks for the extra background. A copy of the 168 code would be useful.

As it happens, some of the code I need to work on today involves sending

I2C data from a Pi to an arduino clone. So far I've been sending single

bytes but today I'll try more than one. I'll capture bus activity with the

Open Workbench Logic Sniffer and report results.

I'm sorry you're experiencing problems, but this kind of user feedback is

really valuable. Either our code is doing something wrong, (in which case

we need to find and fix it), or you are (in which case our documentation is

defective, and we need to fix that).

I've copied this to our user group, just in case someone else has

similar problems or has an idea of what's going wrong.

On 26 February 2013 00:53, davec2 [email protected] wrote:

Sorry for the delay in responding!

That's curious - I am writing to an Atmel Atmega168, which has been

programmed to send and receive multiple bytes via I2C. I've tested the

Atmega168 and found it to work with other devices. But when I tried to

write to it with the quick2wire api, I couldn't get more than 2 bytes to go

across at a time. I confirmed this with an I2C protocl analyzer (a

gabotronics xprotolab). I wonder what is different? I'm tempted to send one

of these to you to see if you can get it to work.

The code I have running on the Atmega168 is on my other computer -I'll get

it out to you as an attachment if you want.

Cheers,

Dave

Date: Sun, 17 Feb 2013 07:25:06 -0800

From: [email protected]

To: [email protected]

CC: [email protected]

Subject: Re: [quick2wire-python-api] I2C writing and writing_bytes cannot

seem to write more than 2 bytes of data at a time (#27)

I've just done a test writing and reading multiple bytes to a 24LC512

eeprom, and everything works as expected.

Dave, what is the device you are writing to?

Reply to this email directly or view it on GitHub.

Reply to this email directly or view it on GitHubhttps://github.com//issues/27#issuecomment-14086245

.

http://quick2wire.com safe, simple connection to your Raspberry Pi


Reply to this email directly or view it on GitHub.
#include <util/twi.h>
#include <avr/io.h>

#define SET(x,y) (x|=(1<<y))
#define CLR(x,y) (x&=(~(1<<y)))
#define CHK(x,y) (x&(1<<y))
#define TOG(x,y) (x^=(1<<y))

#define sbi(x,y) x |= _BV(y) //set bit
#define cbi(x,y) x &= ~(_BV(y)) //clear bit
#define tbi(x,y) x ^= _BV(y) //toggle bit
#define is_high(x,y) ((x & _BV(y)) == _BV(y))

#define F_CPU 8000000UL
#define SLEEP() asm("SLEEP")

#include <util/delay.h>
#include <avr/interrupt.h>
//global variables
#define BUFLEN_RECV 5
uint8_t r_index =0;
uint8_t recv[BUFLEN_RECV]; //buffer to store received bytes

#define BUFLEN_TRAN 3
uint8_t t_index=0;
//test bytes to transmit
uint8_t tran[BUFLEN_TRAN] = {0x12, 0x34, 0x56};

//variable to indicate if something went horribly wrong
uint8_t reset=0;

int timer0_overflow_count;
volatile unsigned char timer0_overflow_flag;

//globals for our input packet
// recv[0] is new_cmd; 1 for new_cmd; 0 for when executed
// recv[1] is motor; 0,1,2,3,4,5 or 6 for respective motor
// recv[2] is speed; 255 for slowest, 0 for fastest
// recv[3] and recv[4] = dur_1 and dir_0
//
// dur_1 and dur_0 make a two byte value for duration in milliseconds
// that is, duration of how long the motor is on at the desired speed.
// the two bytes are decimal coded - that is, only acceptable values are numerics
// from 00 to 99 for each byte. This means the largest duration is 9999 msec, or just under
// 10 seconds. This should be ok??
// Isn't there a way to easily convert hex to decimal?
//

char new_cmd = 0;
char motor = 0;
char speed = 255;
char dur_1,dur_0;

//setup the I2C hardware to ACK the next transmission
//and indicate that we've handled the last one.
#define TWACK (TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWEA))
//setup the I2C hardware to NACK the next transmission
#define TWNACK (TWCR=(1<<TWINT)|(1<<TWEN))
//reset the I2C hardware (used when the bus is in a illegal state)
#define TWRESET (TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO)|(1<<TWEA))

// ******************************
// ** define ISRs
// **
// ******************************

/*SIGNAL(SIG_OVERFLOW0)
{
timer0_overflow_count++;
timer0_overflow_flag = 1; // flag the overflow
}

*/

ISR(TIMER0_OVF_vect)
{
timer0_overflow_count++;
timer0_overflow_flag = 1; // flag the overflow
}

ISR(TIMER1_COMPA_vect)
{
//based on the global variable for motor, we toggle a specific bit of PORTB to
// activate the appropriate motor. The TOG function linked to the ISR TIMER1_COMPA_vect
// will make the appropriate PORTB bit act like a PWM output.
switch(motor)
{
case(0):
SET(PORTB,6);break;
case(1):
SET(PORTB,5);break;
case(2):
SET(PORTB,4);break;
case(3):
SET(PORTB,3);break;
case(4):
SET(PORTB,2); break;
//case(5):
// TOG(PORTB,0); break;
default:
// do nothing
break;
}

}

ISR(TIMER1_OVF_vect)
//based on the global variable for motor, we toggle a specific bit of PORTB to
// activate the appropriate motor. The TOG function linked to the ISR TIMER1_OVF_vect
// will make the appropriate PORTB bit act like a PWM output.
{
switch(motor)
{
case(0):
CLR(PORTB,6);break;
case(1):
CLR(PORTB,5);break;
case(2):
CLR(PORTB,4);break;
case(3):
CLR(PORTB,3);break;
case(4):
CLR(PORTB,2); break;
//case(5):
// TOG(PORTB,0); break;
default:
// do nothing
break;
}

}

//
// functions
//
unsigned long millis()
{
unsigned long count;
do
{
timer0_overflow_flag = 0; // clear the overflow flag
count = timer0_overflow_count; // read timer0_overflow_counter
}
while (timer0_overflow_flag); // if an overflow occurred during the read, try again

return count * 64UL * 2UL / (F_CPU / 128000UL);

}

void delayms( uint16_t millis ) {
while ( millis ) {
_delay_ms( 1 );
millis--;
}
}

void handleI2C(){
//check if we need to do any software actions
if(CHK(TWCR,TWINT)){
switch(TW_STATUS){
//--------------Slave receiver------------------------------------
//SLA_W received and acked, prepare for data receiving
case 0x60:
TWACK;
//r_index =0;
break;
case 0x80: //a byte was received, store it and
//setup the buffer to recieve another
recv[r_index] = TWDR;
r_index++;
//don't ack next data if buffer is full
if(r_index >= BUFLEN_RECV){
TWNACK;
}else {
TWACK;
}
break;
case 0x68://adressed as slave while in master mode.
//should never happen, better reset;
reset=1;
case 0xA0: //Stop or rep start, reset state machine
TWACK;
break;
//-------------- error recovery ----------------------------------
case 0x88: //data received but not acked
//should not happen if the master is behaving as expected
//switch to not adressed mode
TWACK;
break;
//---------------Slave Transmitter--------------------------------
case 0xA8: //SLA R received, prep for transmission
//and load first data
t_index=1;
TWDR = tran[0];
TWACK;
break;
case 0xB8: //data transmitted and acked by master, load next
TWDR = tran[t_index];
t_index++;
//designate last byte if we're at the end of the buffer
if(t_index >= BUFLEN_TRAN) TWNACK;
else TWACK;
break;
case 0xC8: //last byte send and acked by master
//last bytes should not be acked, ignore till start/stop
//reset=1;
case 0xC0: //last byte send and nacked by master
//(as should be)
TWACK;
break;
//--------------------- bus error---------------------------------
//illegal start or stop received, reset the I2C hardware
case 0x00:
TWRESET;
break;
}
}
}

int true_convert(char dur_1, char dur_0)
{
// convert dur_1 | dur_0 to a decimal integer
int actual;

actual = dur_1*100 + dur_0;
return actual;

// maybe could do some limit checking here or accept other conversions?

}

//---------------MAIN---------------------------------------------
int main(){
int dly = 0;
//load slave address
TWAR = (0x01<<1); //we're using address 0x01
//enable I2C hardware
TWCR = (1<<TWEN)|(1<<TWEA);
// initially set all I/O related to OCRnx to input
// for Atmega168, we have the following
// OC2B = PD3
// OC2A = PB3
// OC0B = PD5
// OC0A = PD6
// OC1A = PB1
// OC1B = PB2

//DDRD = 0b10010111;
DDRB = 0xff;

// setup the PWM counters

TCCR1A = 0b00000001; // turn off the ocr1a/1b output as normal startup condition. TCCR1A = 0b10100001; // 
TCCR1B = 0b00001011; // 
TIMSK1 = 0x00; // enable interrupt on ocie1a - match on ocr1a - this will be used to toggle another port

// timer 0 is used for the delayms function
// TCCR0A = 0b10100011;
// TCCR0B = 0b00000011; //fast PWM, clk / 64
// need to add TCCR2A and B to make 6 PWM output signals

//TCCR2A = 0b10100011;
//TCCR2B = 0b00000100;
// some comments: (1) timer0 is being used for both a pwm and for the delayms function. Can have only
// one mode and one use at a time. if as delay, then not also as PWM - we lose two pwm outputs here. 
//
// changing the ddr of a port might do nothing to the ocx outputs: instead, we will likely need to
// turn on and off the counter itself using TTC1n registers.
//
// isn't there an sbi() assembly command that allows one to toggle any port bit based on a counter??
// maybe we can gain back the two outputs by toggling other port bits (on command of counter 2 and 
// counter 1, based on the command sent over i2c?

sei();

while(1){
while(r_index <5)
{
handleI2C();
}
new_cmd = recv[0];
motor = recv[1];
speed = recv[2];
dur_1 = recv[3];
dur_0 = recv[4];
//OCR0A = 0;
//OCR0B = 0;
OCR1A = 0;
OCR1B = 0;
//OCR2A = 0;
//OCR2B = 0;

if (new_cmd == 1)
{
//DDRB = 0xff;
new_cmd = 0; // reset to 0 so that only a newly sent command will get us back here
// reset what we received from i2c:
recv[0]=0;recv[1]=0;recv[2]=0;recv[3]=0;recv[4]=0;
dly = true_convert(dur_1,dur_0);
TCCR1A = 0b10000001; // turn on compare match output of counter
TCNT1 = 0xff; // to ensure we always get the proper polarity of our PWM
OCR1A = speed; // set the PWM speed
TIMSK1 = 0x03;
delayms(dly); // allow to run for specified amount of time
TCCR1A=0x01; // disable the PWM output
TIMSK1 = 0x00;
PORTB = 0x00; // make certain all the motors are OFF
r_index = 0; // get ready for next i2c transaction
// the problem with this is that until we complete the one motor command,
// we will be unable to see a new command.
// so the host should likely write a motor command (5 bytes), then
// read a status [polling]. When that status is done, then can write another
// command.

//
// all because neither arduPi nor quick2wire-python-api can apparently reliably
// send more than one byte at a given i2c write (!!!)


//DDRB = 0x00;
// 
// ISR(TIMER1_COMPA_vect) selects the appropriate bit to toggle based on motor value. 

}
}
}
//-----------END MAIN---------------------------------------------

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants