I2C en atmega168 / 328 usando interrupciones

0

Tengo la siguiente configuración I2C con un ATmega168 (1 MHz) como maestro y un Arduino Uno (16 MHz) como esclavo.

El comando para el LED conectado al esclavo proviene del maestro y viceversa. No consigo que el I2C funcione. Los LED están en cualquier valor que inicialmente puse ledValue, y no lo está obteniendo del esclavo / maestro.

El código que se ejecuta en el maestro.

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

volatile uint8_t slaveAddress = 5;
volatile uint8_t ledValue = 0;
volatile uint8_t refValue = 100;
volatile uint8_t rw;

void send_start(){ TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWSTA) | (1 << TWEN) | (1 << TWIE); }

void send_stop(){ TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWSTO) | (1 << TWEN) | (1 << TWIE); }

void send_data(){ TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); }

ISR(TWI_vect)
{
    volatile uint8_t status = TWSR & 0xF8;
    if(status == 0x08) // start send successfully - now send SLA+R/W
    {
        TWDR = (slaveAddress << 1) + rw;
        send_data();
        return;
    }
    if(status == 0x50 || status == 0x58) //receive mode - data received - read it - send stop
    {
        ledValue = TWDR;
        send_stop();
        return;
    }
    if(status == 0x18 || status == 0x20) //transmit mode - SLA+W successfully sent - now send data
    {
        TWDR = refValue; //data to be transmitted
        send_data();
        return;
    }
    if(status == 0x28 || status == 0x30) //transmit mode - data successfully sent - now send stop
    {
        send_stop();
        return;
    }
    //anything else - clear TWINT
    TWCR |= (1 << TWINT);
}

void initialize_pwm()
{
    DDRD |= (1 << DDD3);
    TCCR2A |= (1 << COM2B1) | (1 << WGM20);
    TCCR2B |= (1 << CS20);
}

int main(void)
{
    initialize_pwm();
    TWBR = 0;
    //TWSR &= ~((1 << TWPS1) | (1 << TWPS0)); // prescaler 1
    // SCL = 1 MHz/(16 + 2*0*1) = 1/16 MHz
    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
    sei();

    while(1)
    {
        rw = 1; //read
        send_start();
        _delay_ms(1000);
        OCR2B = ledValue;
        rw = 0; //write
        send_start();
    }
    return 0;

}

El código que se ejecuta en el esclavo.

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

volatile uint8_t slaveAddress = 5;
volatile uint8_t ledValue = 0;
volatile uint8_t refValue = 100;

void send_data(){ TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); }

ISR(TWI_vect)
{
    volatile uint8_t status = TWSR & 0xF8;
    if(status == 0xA8) // trasmit mode - master sent SLA+R - now send data back
    {
        TWDR = refValue; //data to be transmitted
        send_data();
        return;
    }
    if(status == 0x80 || status == 0x88) // receive mode - master sent data after SLA+W - read it
    {
        ledValue = TWDR;
        TWCR |= (1 << TWINT);
        return;
    }
    //anything else - clear TWINT
    TWCR |= (1 << TWINT);
}

void initialize_pwm()
{
    DDRD |= (1 << DDD3);
    TCCR2A |= (1 << COM2B1) | (1 << WGM20);
    TCCR2B |= (1 << CS20);
}

int main(void)
{
    initialize_pwm();
    TWAR = (slaveAddress << 1);
    TWCR = (1 << TWINT) | (1 << TWEN) | ( 1 << TWIE);
    sei();

    while(1)
    {
        _delay_ms(1000);
        OCR2B = ledValue;
    }

}
    
pregunta Suba Thomas

2 respuestas

1

He podido llegar a una respuesta a mi propia pregunta.

Código maestro.

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

volatile uint8_t ledValue = 0;
volatile uint8_t refValue = 0;
volatile uint8_t status;
volatile uint8_t pstatus = 0;
volatile uint8_t rw;
volatile bool receiveQ = true;
volatile bool transmitQ = false;

void send_start(){ TWCR &= ~(1 << TWSTO); TWCR |= (1 << TWINT) | (1 << TWSTA); }

void send_stop(){ TWCR &= ~(1 << TWSTA);  TWCR |= (1 << TWINT) | (1 << TWSTO); }

void send_data(){ TWCR &= ~((1 << TWSTA) | (1 << TWSTO)); TWCR |= (1 << TWINT); }

void toggle(){PORTB ^= (1 << PORTB0);}

ISR(TWI_vect)
{
    status = TWSR & 0xF8;
    if(status == 0x08 || status == 0x10)
    {
        pstatus = 1;
        TWDR = rw;
        send_data();
        return;
    }
    if(status == 0x38)
    {
        pstatus = 2;
        send_start();
        return;
    }
    if(status == 0x40)
    {
        pstatus = 3;
        TWCR &= ~(1 << TWEA);
        send_data();
        return;
    }
    if(status == 0x48)
    {
        pstatus = 4;
        send_start();
        return;
    }
    if(status == 0x50)
    {
        pstatus = 5;
        ledValue = TWDR;
        TWCR &= ~(1 << TWEA);
        send_data();
        return;
    }
    if(status == 0x58)
    {
        pstatus = 6;
        ledValue = TWDR;
        receiveQ = false;
        transmitQ = true;
        send_stop();
        return;
    }
    if(status == 0x18 || status == 0x20)
    {
        pstatus = 7;
        TWDR = refValue; //data to be transmitted
        send_data();
        return;
    }
    if(status == 0x28 || status == 0x30)
    {
        pstatus = 8;
        receiveQ = true;
        transmitQ = false;
        send_stop();
        return;
    }
    if((pstatus == 8 || pstatus == 6)){toggle();}
    pstatus = 0;
    send_stop();
}

void initialize_pwm()
{
    DDRD |= (1 << DDD3);
    TCCR2A |= (1 << COM2B1) | (1 << WGM20);
    TCCR2B |= (1 << CS20);
}

int main(void)
{
    DDRB |= (1 << DDB0);
    //uint8_t slaveAddress = 5;
    initialize_pwm();
    TWBR = 10;
    TWCR = (1 << TWEN) | (1 << TWIE);
    sei();

    while(1)
    {
        if(receiveQ)
        {
            rw = 11; //BitShiftLeft[5, 1] + 1, read
            refValue += 5;
            if(refValue > 255){refValue = 0;}
            send_start();
        }
        _delay_ms(100);
        OCR2B = ledValue;
        if(transmitQ)
        {
            rw = 10; //BitShiftLeft[5, 1], write
            send_start();
        }
    }
    return 0;

}

Código de esclavo.

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

volatile uint8_t ledValue = 0;
volatile uint8_t refValue = 0;
volatile uint8_t status;

void send_ack(){ TWCR |= (1 << TWINT) | (1 << TWEA);}

void send_n_ack(){ TWCR &= ~(1 << TWEA); TWCR |= (1 << TWINT);}

void toggle(){PORTB ^= (1 << PORTB0);}

ISR(TWI_vect)
{
    status = TWSR & 0xF8;
    if(status == 0xA8 || status == 0xB0)
    {
        TWDR = refValue;
        send_n_ack();
        return;
    }
    if(status == 0xB8)
    {
        TWDR = refValue;
        send_n_ack();
        return;
    }
    if(status == 0xC0 || status == 0xC8)
    {
        send_ack();
        return;
    }
    if(status == 0x60 || status == 0x68 || status == 0x70|| status == 0x78)
    {
        send_ack();
        return;
    }
    if(status == 0x80 || status == 0x90)
    {
        ledValue = TWDR;
        send_n_ack();
        return;
    }
    if(status == 0x88 || status == 0x98 || status == 0xA0)
    {
        send_ack(); 
        return;
    }
    toggle();
    send_ack();
}

void initialize_pwm()
{
    DDRD |= (1 << DDD3);
    TCCR2A |= (1 << COM2B1) | (1 << WGM20);
    TCCR2B |= (1 << CS20);
}

int main(void)
{
    DDRB |= (1 << DDB0);
    initialize_pwm();
    TWAR = 10; //BitShiftLeft[5, 1];
    TWCR = (1 << TWEN) | ( 1 << TWIE) | (1 << TWEA);
    sei();

    while(1)
    {
        refValue += 5;
        if(refValue > 255){refValue = 0;}
        _delay_ms(100);
        OCR2B = ledValue;
    }

}
    
respondido por el Suba Thomas
0

Por lo que recuerdo, la comunicación I2C no funcionará dentro de un ISR. Muchas otras cosas tampoco funcionan (correctamente) dentro de un ISR, por ejemplo, digitalWrite. Hace algún tiempo intentaba usar un sensor MPU6050 (I2C) con llamadas de función dentro de un ISR, pero eso no funcionó, así que tuve que cambiar a un sensor analógico. Simplemente busque "llamada de función de ISR" para confirmar.

    
respondido por el Dave

Lea otras preguntas en las etiquetas