Problemas en la transmisión a través de I2C desde ATtiny85

0

Estoy enviando un byte a través de I2C desde un esclavo ATtiny85. Lo estoy probando con un maestro ATmega328.

La secuencia debe ser START, SLA + R, ACK, datos, NACK , STOP. El código funciona solo cuando la secuencia es START, SLA + R, ACK, datos, ACK , STOP. Específicamente, en el código maestro a continuación, debe ser receive_i2c_data (val, false) y no receive_i2c_data (val, true). Las últimas obras, las primeras no. ¿Por qué es eso.

Además, a veces el código no funciona en la primera descarga. Necesito descargarlo de nuevo.

Código ATtiny85 de esclavo

#include <stdint.h>
#include <avr/interrupt.h>

#define SLAVE_ADDRESS 0x40

#define TIMER_OVERFLOW_COUNT  279
volatile uint8_t i2cstage = 0;
void reset_i2c()
{
    USISR = 0xF0;
    USICR = (1 << USISIE | 1 << USIWM1) | 1 << USICS1;
    PORTB |= 1 << PORTB0;
    PORTB |= 1 << PORTB2;
    DDRB |= 1 << DDB2;
    DDRB &= ~(1 << DDB0);
    i2cstage = 0;
}
#define transmit_i2c_data()  {DDRB |= 1 << DDB0;USISR = 0xF0;}
#define transmit_i2c_ACK()  {DDRB |= 1 << DDB0;USIDR = 0x00;USISR = 0xFE;}
#define receive_i2c_ACKNACK()  {DDRB &= ~(1 << DDB0);USISR = 0xFE;}
ISR(USI_START_vect)
{
    USISR |= 1 << USISIF;
    USICR |= 1 << USIOIE;
    i2cstage = 1;
}
volatile uint8_t i2c_0 = 0;
ISR(USI_OVF_vect)
{
    uint8_t data = USIDR;
    switch ( i2cstage)
    {
        case 1:
            if( data == (SLAVE_ADDRESS << 1) + 1)
            {
                i2cstage = 2;
                transmit_i2c_ACK();
            }
            else
            {
                reset_i2c();
            }
            break;

        case 2:
            i2cstage = 3;
            USIDR = i2c_0;
            transmit_i2c_data();
            break;

        /*case 3:
            i2cstage = 4;
            receive_i2c_ACKNACK();
            break;*/

        default:
            reset_i2c();
            break;
    }
}
volatile uint16_t timerCount = TIMER_OVERFLOW_COUNT;
ISR(TIMER1_OVF_vect)
{
    timerCount++;
}
int main(void)
{
    reset_i2c();
    TIMSK |= 1 << TOIE1;
    TCCR1 |= 1 << PWM1A;
    OCR1C = 27;
    sei();
    TCCR1 |= (1 << CS13) | (1 << CS11) | (1 << CS10);
    while( 1)
    {
        if( timerCount >= TIMER_OVERFLOW_COUNT)
        {
            timerCount = 0;
            if(++i2c_0 > 5)
                 i2c_0 = 0;
        }
        if( (USISR & (1 << USIPF)) == (1 << USIPF))
        {
            USISR = 1 << USIPF;
            reset_i2c();
        }
    }
    return 0;
}

Código Master ATmega328

#include <stdint.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <util/delay.h>

#define SLAVE_ADDRESS 0x40

volatile bool i2cCompletedQ = true;
void wait_for_i2c()
{
    while( !(TWCR & 1 << TWINT))
    {
    }
    if( TWCR & 1 << TWINT)
    {
        i2cCompletedQ = true;
    }
    else
    {
        i2cCompletedQ = false;
    }
}
void transmit_i2c_start()
{
    if( !i2cCompletedQ)
    {
        return;
    }
    TWCR = 1 << TWINT | 1 << TWEN | 1 << TWSTA;
    wait_for_i2c();
}
void transmit_i2c_stop()
{
    if( !i2cCompletedQ)
    {
        return;
    }
    TWCR = 1 << TWINT | 1 << TWEN | 1 << TWSTO;
}
void transmit_i2c_data(uint8_t data)
{
    if( !i2cCompletedQ)
    {
        return;
    }
    TWDR = data;
    TWCR = 1 << TWINT | 1 << TWEN;
    wait_for_i2c();
}
void receive_i2c_data(uint8_t *odata, bool ack)
{
    if( !i2cCompletedQ)
    {
        return ;
    }
    if( ack)
    {
        TWCR = (1 << TWINT | 1 << TWEN) | 1 << TWEA;
    }
    else
    {
        TWCR = 1 << TWINT | 1 << TWEN;
    }
    wait_for_i2c();
    *odata = TWDR;
}
void update_i2c_Input(uint8_t *val)
{
    transmit_i2c_start();
    transmit_i2c_data((SLAVE_ADDRESS << 1) + 1);
    receive_i2c_data(val, true);//should be false?
    transmit_i2c_stop();
}
int main(void)
{
    TWBR = 100;
    TWSR &= ~(1 << TWPS1 | 1 << TWPS0);

    DDRD |= 1 << DDD6;
    TCCR0A |= 1 << COM0A1;
    TCCR0A |= 1 << WGM00;
    sei();
    TCCR0B |= (1 << CS02) | (1 << CS00);
    uint8_t val = 0;
    while( 1)
    {
        i2cCompletedQ = true;
        update_i2c_Input(&val);
        OCR0A = (val * 255) / 5;
        _delay_ms(1000);
    }
    return 0;
}
    
pregunta Suba Thomas

0 respuestas

Lea otras preguntas en las etiquetas