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

Weird address issues on ATtiny2313-10PU #4

Open
iostat opened this issue Sep 22, 2014 · 14 comments
Open

Weird address issues on ATtiny2313-10PU #4

iostat opened this issue Sep 22, 2014 · 14 comments

Comments

@iostat
Copy link

iostat commented Sep 22, 2014

EDIT: I may be doing something wrong with my code, however, I've found that when configuring my ATtiny2313V-10PU to be an I2C slave on 0x40, probing the I2C bus with my raspberry pi using i2cdetect sometimes gets hits on just 0x40, sometimes on 0x40 and 0x41, sometimes on 0x40, 0x41, AND 0x42, and sometimes even 0x40, 0x41, 0x42, and 0x43.

I've blanked out this issue in the meantime while I investigate even further, but I have a feeling that something fishy is going on here. Here's my original code. I can confirm that that the address select pins are giving me a get_i2c_address() of 0b01000000 (0x40) using bit_bang_spi to drive a shift-register based LED driver. i2c_slave.h is of course, usitwislave.h

#include <avr/io.h>

#include "spi.h"
#include "i2c_slave.h"

#define REGISTER_COUNT 32
volatile uint8_t registers[REGISTER_COUNT];

void init_gpio()
{
    // ------------- ADDRESS SELECT -------------
    // set PA0 as input (address select pins)
    DDRA &= ~(1 << 0);

    // Set PD2,3,4,5 as input (address selection pins)
    DDRD &= ~(1 << 2);
    DDRD &= ~(1 << 3);
    DDRD &= ~(1 << 4);
    DDRD &= ~(1 << 5);

    // Enable pullups for address selection pins (PA0, PD2,3,4,5).
    PORTA |= (1 << 0);
    PORTD |= (1 << 2);
    PORTD |= (1 << 3);
    PORTD |= (1 << 4);
    PORTD |= (1 << 5);

    // -------------- BIT-BANG SPI --------------
    // set PA1 as output (LED driver latch-enable)
    DDRA |= (1 << 1);

    // Set PD0,1 as output (LED driver, serial clock and data)
    DDRD |= (1 << 0);
    DDRD |= (1 << 1);

    // ------------- ROTARY ENCODER -------------
    // Set PB0,1,2 as input
    DDRB &= ~(1 << 0);
    DDRB &= ~(1 << 1);
    DDRB &= ~(1 << 2);

    // Enable pullups for rotary encoder pins
    PORTB |= (1 << 0);
    PORTB |= (1 << 1);
    PORTB |= (1 << 2);
}

inline unsigned char get_i2c_address()
{
    unsigned char base = 0b01000000;
    base |= ((PINA & (1 << 0)) << 4); // mask out PA0 and shift to 5th bit
    base |= ((PIND & (1 << 2)) << 1); // mask out PD2 and shift to 4th bit
    base |= ((PIND & (1 << 3)) >> 1); // mask out PD3 and shift to 3rd bit
    base |= ((PIND & (1 << 4)) >> 3); // mask out PD4 and shift to 2nd bit
    base |= ((PIND & (1 << 5)) >> 5); // mask out PD5 and shift to 1st bit

    return base;

}

void idle_callback(void)
{
    //bit_bang_spi(get_i2c_address());
}

void i2c_callback(uint8_t input_buffer_length,
            const uint8_t* input_buffer, uint8_t* output_buffer_length,
            uint8_t* output_buffer)
{
    if(input_buffer_length > 0) {
        *output_buffer_length = 1;

        if(input_buffer[0] < REGISTER_COUNT) {
            *output_buffer = registers[input_buffer[0]];
        } else {
            *output_buffer = 0xFF;
        }
    }
}

void main(void)
{
    init_gpio();

    for(int i = 0; i < REGISTER_COUNT; i++) {
        registers[i] = i;
    }

    usi_twi_slave(get_i2c_address(), 0, i2c_callback, idle_callback);

    while(1);
}
@iostat iostat changed the title Is this library broken on ATtiny2313? Weird address issues on ATtiny2313-10PU Sep 22, 2014
@eriksl
Copy link
Owner

eriksl commented Sep 22, 2014

Just a wild guess. I've never used this on an attint2313, but the 2313
is, afaik, of an older generation than the '85 and '861 I've been using.
Maybe the USI implementation also differs.

Also, please note that anytime i2c doesn't get a proper answer (i.e.
"NAK") it will return 0xff as data, so 0xff definitely isn't a good
value for probing.

@iostat
Copy link
Author

iostat commented Sep 28, 2014

I've just gotten my hands on a couple more chips, including a 2313A, believing that perhaps I had a faulty chip. I've taken a look at the USI section of the datasheets for both the '2313A and the '85 and they appear identical. There's nothing in the errata either. On a raspberry pi now, it cannot find my ATtiny at all, however it can find other devices on the same bus (see below for devices tested against).

I've created a TWI master test setup, and am using the following code on an ATmega328P with Peter Fleury's I2C Master library. I'm running 5V logic level, with 4.7kOhm pullups. It finds a read address on 0x40 but crashes when it tries to write on 0x40. I've tested this scan code against a couple of "real" I2C devices on the same bus, a DS1307 (at 0x68), and two 24LC256 (at 0x53 and 0x57) EEPROMs and it can find all of them with one sweep if the ATtiny is disconnected. I've also tried commenting out init_gpio, thinking that perhaps it's inadvertently messing something up somehow to no avail:

// Probes the i2c bus for devices.
void i2c_scan(void)
{
    i2c_init();

    printf("%S", I2C_SCAN_HEADER);
    printf("%S", I2C_SCAN_TABLE_SEPARATOR);
    for(unsigned char high_addr = 0x00; high_addr <= 0x70; high_addr += 0x10) {
        printf("|%X_|", high_addr>>4);
        for(unsigned char low_addr = 0x00; low_addr <= 0xF; low_addr++) {
            unsigned char address = (high_addr|low_addr) << 1;
            unsigned char access_status = i2c_start(address+I2C_READ);

            if(access_status == 0) {
                putchar('R');

                // if we can hit a read address, we actually need to read something and send a NACK
                i2c_readNak();
            } else {
                putchar(' ');
            }

            i2c_stop();

            access_status = i2c_start(address + I2C_WRITE);
            i2c_stop();

            if(access_status == 0) {
                putchar('W');
            } else {
                putchar(' ');
            }

            printf("|");

            _delay_ms(100);
        }

        if(high_addr != 0x70) {
            printf("%S", I2C_SCAN_TABLE_SEPARATOR);
        }
    }

    printf("%S", I2C_SCAN_TABLE_FOOTER);
}

@eriksl
Copy link
Owner

eriksl commented Sep 28, 2014

I find some unexplainable problems come from marginal power supply, you
might want to check that. The voltage regulator should have enough
(somewhat more voltage in than required) headroom (and also enough
power/current supply) to catch up with the temporary larger power
demands. Also do you have a 100 nF capacitator over +/- in the immediate
neighbourhood of the microcontroller?

I am running this code for a few years now, so I guess it can work ;-) I
now runs on a '861 but before it has been running on a '85.

You might want to try a crystal (or even ceramic resonator), but in
theory it shouldn't be necessary for i2c operation (and mine runs fine
without).

@iostat
Copy link
Author

iostat commented Sep 28, 2014

Power's all good and everything's appropriately decoupled. I've taken a peek over the code as well as my scanner and i2cdetect (raspberry pi's). I may have an idea of what's going on. Have you ever tested a stop condition immediately after start_[device-write-address]? I can't find anything in your library that would seem to handle something like that but my mind may be shutting it out after staring at code for days now :P. It might explain why the raspberry pi thinks consecutive address are showing up (in reality, the i2c slave is sending ACKs thinking it's getting data when the raspberry pi is thinking it's getting ACKs from probing out the next addresses)

If such functionality doesn't exist, I'd be happy to try and implement it and submit a patch :)

@eriksl
Copy link
Owner

eriksl commented Sep 28, 2014

If such functionality doesn't exist, I'd be happy to try and implement
it and submit a patch :)

Nice.

Please note that twi does feature a start condition interrupt, it does
NOT feature a stop condition interrupt (which is a serious ommitment
according to many). A stop condition can be detected though, but iirc it
isn't always detected properly.

You might want to try it with power saving off, by the way (no idle
mode). When the cpu is in idle mode, a stop condition may arrive and go
by unnoticed (as there is no interrupt).

@iostat
Copy link
Author

iostat commented Sep 28, 2014

Yep, all of my tests were with idle disabled. I'm definitely going to to try and revisit this when I have some time. For now I guess I'll have to suffice with a dummy write when testing for devices.

I'm guessing the fix is going to be a little more involved than the following, although it did end up scanning successfully (haven't tested how it actually affects the buffer states)

        case(of_state_request_ack):
        {
            of_state = of_state_check_ack;

            USIDR       = 0x00;
            set_counter = 0x0e;                 //  receive 1 bit (2 edges)
            set_sda_to_input();                 //  initiate receive ack

            // if there's a stop condition, however...
            if((PORT_USI & (_BV(PIN_USI_SDA))) && (PORT_USI & (_BV(PIN_USI_SCL))))
                twi_reset();

            break;
        }

@akohlsmith
Copy link

I'm (really) late to the party but I have noticed on the tiny25 that if you want anything even remotely close to 400kHz I2C bus speed you've got to be running up near 64MHz. I was quite surprised that even at 8MHz (internal RC, no PLL, CLKDIV8 not set) 100kHz was iffy. As soon as I enabled the PLL things just started working properly.

@gismofx
Copy link

gismofx commented Aug 25, 2015

@akohlsmith Where are you enabling the PLL? Can you post the line of code? I had some issues as well, and I started running my ATTINY85 with a 16mhz external crystal for needing more accurate timing. It improved, but on occasion I get some problems. I'd like to try.

@eriksl
Copy link
Owner

eriksl commented Aug 25, 2015

Yes confirmed. You need to run > 8 Mhz to make I2C reliable. It can be done quite easily with the PLL clock (which the larger models don't have...), without a crystal. I2C is not sensitive to timing, so it doesn't matter that the RC clock isn't accurate. It just needs to be FAST, at least with the USI-implementation. The problem is in the acknowledgement of the address after a start condition. It's very easy to take to long for that and the master will give up. Unfortunately the USI-implementation cannot acknowledge in hardware. There is another problem with USI and that's there is no interrupt for STOP conditions. So you need to rely on STOP conditions always coming right before a START condition. Or monitor the line all of the time, but that's not what we want.

@akohlsmith
Copy link

@gismofx it's done in the fuses. http://www.engbedded.com/fusecalc/ shows that CKSEL=0001 will select the PLL (on ATTINY25).

@akohlsmith
Copy link

@eriksl that seems odd; USI's supposed to be taking the hard part away from software. I'm almost positive that I could bit-bang an I2C slave if I was running at 64MHz. No matter, I've got it figured out now. Do you think it might be worth adding a note to that in the readme?

@eriksl
Copy link
Owner

eriksl commented Aug 25, 2015

Funny you mention that. Definitely more people have mentioned this fact. The USI isn't helping very much indeed, I also found out. If you're going to do a full-blown I2C slave, you'd better switch to an atmega with full I2C support. The USI implementation would be sufficient for an I2C master though.

On a side note, how did you manage to get it running at 64 Mhz??? I am sure you can get the clock that high using the PLL, but it isn't supposed to work (max 20 Mhz!).

@akohlsmith
Copy link

I'm not doing anything spectacular. The ATTINY25's PLL takes a source clock and multiplies it by 8. The default input clock is the 8MHz internal RC oscillator. Section 6.1.5 of the datasheet. I am guessing that the 20MHz limit is the clock pin input limit.

@eriksl
Copy link
Owner

eriksl commented Aug 28, 2015

Or the clock is limited internally! Are you sure it really runs at the full 64 Mhz? With limited voltage (e.g. 3.3V) it can't even run on 20 Mhz...

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

4 participants