Napisałem driver do TWI(I2C). Jestem Ci wdzięczny ponieważ poprzez tą dyskusję na temat volatile
poprawiłem trochę kod z takiego:
/* program Atmega8L */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/twi.h>
#include "lcd44780.h"
#include <util/atomic.h>
/* define slave (MPU6050) address as 0x68 because AD0 is low */
#define SLAVE_ADDRESS (0x68)
#define WHO_AM_I (0x75)
#define TWI_BUFFER_SIZE (1<<5)
#define NO_ERROR (0)
#define OVERFLOW_ERROR (1)
#define READ (0)
#define WRITE (1)
uint8_t data = 0;
uint8_t data_retry = 0;
typedef enum
{
IDLE,
START,
REPEATED_START,
SLA_W_TRANSMIT,
RA_TRANSMIT,
DATA_TRANSMIT,
SLA_R_TRANSMIT,
DATA_RECEIVE,
TRANSMISSION_ERROR
} sm_t;
typedef struct
{
uint8_t reg_address;
uint8_t rw;
uint8_t transmit_value;
uint8_t * receive_value_ptr;
} data_t;
typedef struct
{
data_t buffer[TWI_BUFFER_SIZE];
uint8_t head;
uint8_t tail;
sm_t sm;
} twi_t;
volatile twi_t twi;
/* function start TWI operations */
void twi_start()
{
if (twi.sm == IDLE)
{
twi.sm = START;
TWCR |= (1 << TWSTA) | (1 << TWINT);
TWCR &= ~(1 << TWSTO);
}
}
/* function add data to send to the buffer */
uint8_t twi_send(uint8_t address, uint8_t value)
{
uint8_t return_value;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
if (((twi.head + 1) & (TWI_BUFFER_SIZE - 1)) == twi.tail)
return_value = OVERFLOW_ERROR;
else
{
twi.buffer[twi.head].reg_address = address;
twi.buffer[twi.head].rw = WRITE;
twi.buffer[twi.head].transmit_value = value;
twi.head = ((twi.head + 1) & (TWI_BUFFER_SIZE - 1));
return_value = NO_ERROR;
twi_start();
}
}
return return_value;
}
/* function add pointer for data receive to the buffer */
uint8_t twi_receive(uint8_t address, uint8_t * ptr)
{
uint8_t return_value;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
if (((twi.head + 1) & (TWI_BUFFER_SIZE - 1)) == twi.tail)
return_value = OVERFLOW_ERROR;
else
{
twi.buffer[twi.head].reg_address = address;
twi.buffer[twi.head].rw = READ;
twi.buffer[twi.head].receive_value_ptr = ptr;
twi.head = ((twi.head + 1) & (TWI_BUFFER_SIZE - 1));
return_value = NO_ERROR;
twi_start();
}
}
return return_value;
}
/* TWI interrupt service routine */
ISR(TWI_vect)
{
/* checking TWI status */
uint8_t status = TWSR;
switch (status)
{
case TW_START:
{
/* load SLA + W because we want to send register address first */
TWDR = (SLAVE_ADDRESS << 1) | (0 << 0);
/* clear start bit manually */
TWCR &= ~(1 << TWSTA);
/* release interrupt flag */
TWCR |= (1 << TWINT);
/* change state machine */
twi.sm = SLA_W_TRANSMIT;
break;
}
case TW_REP_START:
{
/* if state machine is on the error state than retry all buffer cycle */
/* start next buffer cycle without releasing the bus if state machine is on data transmit or receive */
if ((twi.sm == TRANSMISSION_ERROR) || (twi.sm == DATA_TRANSMIT) || (twi.sm == DATA_RECEIVE) || (twi.sm == START))
{
TWDR = (SLAVE_ADDRESS << 1) | (0 << 0);
TWCR &= ~(1 << TWSTA);
TWCR |= (1 << TWINT);
twi.sm = SLA_W_TRANSMIT;
}
/* if state machine is on the register address transmit state than load SLA_R */
else if (twi.sm == RA_TRANSMIT)
{
TWDR = (SLAVE_ADDRESS << 1) | (1 << 0);
TWCR &= ~(1 << TWSTA);
TWCR |= (1 << TWINT);
twi.sm = SLA_R_TRANSMIT;
}
/* if state machine is on the data transmit or data receiving than start next buffer cycle */
else if ((twi.sm == DATA_TRANSMIT) || (twi.sm == DATA_RECEIVE))
{
TWDR = (SLAVE_ADDRESS << 1) | (0 << 0);
TWCR &= ~(1 << TWSTA);
TWCR |= (1 << TWINT);
twi.sm = SLA_W_TRANSMIT;
}
break;
}
case TW_MT_SLA_ACK:
{
/* load register address */
TWDR = twi.buffer[twi.tail].reg_address;
/* release interrupt flag */
TWCR |= (1 << TWINT);
/* change state machine */
twi.sm = RA_TRANSMIT;
break;
}
case TW_MT_SLA_NACK:
{
/* if no acknowledge than force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
/* change state machine to error state */
twi.sm = TRANSMISSION_ERROR;
break;
}
case TW_MT_DATA_ACK:
{
/* if before operation was register address transmit */
if (twi.sm == RA_TRANSMIT)
{
/* if current requested operation is write register */
if (twi.buffer[twi.tail].rw == WRITE)
{
/* put the essential data on the bus */
TWDR = twi.buffer[twi.tail].transmit_value;
/* release interrupt flag */
TWCR |= (1 << TWINT);
/* change state machine */
twi.sm = DATA_TRANSMIT;
}
/* if current requested operation is read register */
else
{
/* force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
}
}
/* if before operation was essential data transmit */
else if (twi.sm == DATA_TRANSMIT)
{
/* transfer complete - move the tail forward */
twi.tail = ((twi.tail + 1) & (TWI_BUFFER_SIZE - 1));
/* check if we have another byte to send / receive */
if (twi.head != twi.tail)
{
/* force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
}
else
{
/* stop bit transmit and release interrupt flag */
TWCR |= (1 << TWSTO) | (1 << TWINT);
/* change state machine to idle */
twi.sm = IDLE;
}
}
break;
}
case TW_MT_DATA_NACK:
{
/* if no acknowledge than force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
/* change state machine to error state */
twi.sm = TRANSMISSION_ERROR;
break;
}
case TW_MR_SLA_ACK:
{
/* release interrupt flag */
TWCR |= (1 << TWINT);
/* change state machine */
twi.sm = DATA_RECEIVE;
break;
}
case TW_MR_SLA_NACK:
{
/* if no acknowledge than force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
/* change state machine to error state */
twi.sm = TRANSMISSION_ERROR;
break;
}
case TW_MR_DATA_ACK:
{
/* if acknowledge than force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
/* change state machine to error state */
twi.sm = TRANSMISSION_ERROR;
break;
}
case TW_MR_DATA_NACK:
{
/* transfer complete - move data under the pointer */
*(twi.buffer[twi.tail].receive_value_ptr) = TWDR;
/* move the tail forward */
twi.tail = ((twi.tail + 1) & TWI_BUFFER_SIZE);
/* check if we have another byte to send / receive */
if (twi.head != twi.tail)
{
/* force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
}
else
{
/* stop bit transmit and release interrupt flag */
TWCR |= (1 << TWSTO) | (1 << TWINT);
/* change state machine to idle */
twi.sm = IDLE;
}
break;
}
default:
{
break;
}
}
}
/* timer overflow interrupt service routine */
ISR(TIMER0_OVF_vect)
{
}
void TimerInit()
{
/* overflow interrupt enable and start timer on frequency 8MHz / 1024 */
TIMSK |= (1 << TOIE0);
TCCR0 |= (1 << CS00) | (1 << CS02);
}
void TwiInit()
{
/* 100 kHz frequency of TWI module */
TWBR = 32;
/* enable TWI module and interrupt from them */
TWCR = (1 << TWEN) | (1 << TWIE);
}
int main(void)
{
/* display initialization */
lcd_init();
/* TWI structure initialization */
twi.sm = IDLE;
twi.head = 0;
twi.tail = 0;
/* diodes output */
DDRB |= (1 << PB0);
DDRD |= (1 << PD7);
/* diodes turn off */
PORTB |= (1 << PB0);
PORTD |= (1 << PD7);
/* pull up resistor on PD5 input switch */
PORTD |= (1 << PD5);
TimerInit();
TwiInit();
sei();
/* put interesting commands into TWI buffer */
twi_receive(WHO_AM_I, &data);
twi_receive(WHO_AM_I, &data_retry);
while (1)
{
/* if switch pressed */
if (!((PIND >> PD5) & 1))
{
if (data == 0x68)
{
PORTB &= ~(1 << PB0);
}
else
{
PORTB |= (1 << PB0);
}
}
}
}
na taki :
/* program Atmega8L */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/twi.h>
#include "lcd44780.h"
#include <util/atomic.h>
/* define slave (MPU6050) address as 0x68 because AD0 is low */
#define SLAVE_ADDRESS (0x68)
#define WHO_AM_I (0x75)
#define TWI_BUFFER_SIZE (1<<5)
#define NO_ERROR (0)
#define OVERFLOW_ERROR (1)
#define READ (0)
#define WRITE (1)
volatile uint8_t data = 0;
volatile uint8_t data_retry = 0;
typedef enum
{
IDLE,
START,
REPEATED_START,
SLA_W_TRANSMIT,
RA_TRANSMIT,
DATA_TRANSMIT,
SLA_R_TRANSMIT,
DATA_RECEIVE,
TRANSMISSION_ERROR
} sm_t;
typedef struct
{
uint8_t reg_address;
uint8_t rw;
uint8_t transmit_value;
volatile uint8_t * receive_value_ptr;
} data_t;
typedef struct
{
data_t buffer[TWI_BUFFER_SIZE];
uint8_t head;
uint8_t tail;
sm_t sm;
} twi_t;
volatile twi_t twi;
/* function start TWI operations */
void twi_start()
{
if (twi.sm == IDLE)
{
twi.sm = START;
TWCR |= (1 << TWSTA) | (1 << TWINT);
TWCR &= ~(1 << TWSTO);
}
}
/* function add data to send to the buffer */
uint8_t twi_send(uint8_t address, volatile uint8_t value)
{
uint8_t return_value;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
if (((twi.head + 1) & (TWI_BUFFER_SIZE - 1)) == twi.tail)
return_value = OVERFLOW_ERROR;
else
{
twi.buffer[twi.head].reg_address = address;
twi.buffer[twi.head].rw = WRITE;
twi.buffer[twi.head].transmit_value = value;
twi.head = ((twi.head + 1) & (TWI_BUFFER_SIZE - 1));
return_value = NO_ERROR;
twi_start();
}
}
return return_value;
}
/* function add pointer for data receive to the buffer */
uint8_t twi_receive(uint8_t address, volatile uint8_t * ptr)
{
uint8_t return_value;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
if (((twi.head + 1) & (TWI_BUFFER_SIZE - 1)) == twi.tail)
return_value = OVERFLOW_ERROR;
else
{
twi.buffer[twi.head].reg_address = address;
twi.buffer[twi.head].rw = READ;
twi.buffer[twi.head].receive_value_ptr = ptr;
twi.head = ((twi.head + 1) & (TWI_BUFFER_SIZE - 1));
return_value = NO_ERROR;
twi_start();
}
}
return return_value;
}
/* TWI interrupt service routine */
ISR(TWI_vect)
{
/* checking TWI status */
uint8_t status = TWSR;
switch (status)
{
case TW_START:
{
/* load SLA + W because we want to send register address first */
TWDR = (SLAVE_ADDRESS << 1) | (0 << 0);
/* clear start bit manually */
TWCR &= ~(1 << TWSTA);
/* release interrupt flag */
TWCR |= (1 << TWINT);
/* change state machine */
twi.sm = SLA_W_TRANSMIT;
break;
}
case TW_REP_START:
{
/* if state machine is on the error state than retry all buffer cycle */
/* start next buffer cycle without releasing the bus if state machine is on data transmit or receive */
if ((twi.sm == TRANSMISSION_ERROR) || (twi.sm == DATA_TRANSMIT) || (twi.sm == DATA_RECEIVE) || (twi.sm == START))
{
TWDR = (SLAVE_ADDRESS << 1) | (0 << 0);
TWCR &= ~(1 << TWSTA);
TWCR |= (1 << TWINT);
twi.sm = SLA_W_TRANSMIT;
}
/* if state machine is on the register address transmit state than load SLA_R */
else if (twi.sm == RA_TRANSMIT)
{
TWDR = (SLAVE_ADDRESS << 1) | (1 << 0);
TWCR &= ~(1 << TWSTA);
TWCR |= (1 << TWINT);
twi.sm = SLA_R_TRANSMIT;
}
/* if state machine is on the data transmit or data receiving than start next buffer cycle */
else if ((twi.sm == DATA_TRANSMIT) || (twi.sm == DATA_RECEIVE))
{
TWDR = (SLAVE_ADDRESS << 1) | (0 << 0);
TWCR &= ~(1 << TWSTA);
TWCR |= (1 << TWINT);
twi.sm = SLA_W_TRANSMIT;
}
break;
}
case TW_MT_SLA_ACK:
{
/* load register address */
TWDR = twi.buffer[twi.tail].reg_address;
/* release interrupt flag */
TWCR |= (1 << TWINT);
/* change state machine */
twi.sm = RA_TRANSMIT;
break;
}
case TW_MT_SLA_NACK:
{
/* if no acknowledge than force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
/* change state machine to error state */
twi.sm = TRANSMISSION_ERROR;
break;
}
case TW_MT_DATA_ACK:
{
/* if before operation was register address transmit */
if (twi.sm == RA_TRANSMIT)
{
/* if current requested operation is write register */
if (twi.buffer[twi.tail].rw == WRITE)
{
/* put the essential data on the bus */
TWDR = twi.buffer[twi.tail].transmit_value;
/* release interrupt flag */
TWCR |= (1 << TWINT);
/* change state machine */
twi.sm = DATA_TRANSMIT;
}
/* if current requested operation is read register */
else
{
/* force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
}
}
/* if before operation was essential data transmit */
else if (twi.sm == DATA_TRANSMIT)
{
/* transfer complete - move the tail forward */
twi.tail = ((twi.tail + 1) & TWI_BUFFER_SIZE);
/* check if we have another byte to send / receive */
if (twi.head != twi.tail)
{
/* force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
}
else
{
/* stop bit transmit and release interrupt flag */
TWCR |= (1 << TWSTO) | (1 << TWINT);
/* change state machine to idle */
twi.sm = IDLE;
}
}
break;
}
case TW_MT_DATA_NACK:
{
/* if no acknowledge than force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
/* change state machine to error state */
twi.sm = TRANSMISSION_ERROR;
break;
}
case TW_MR_SLA_ACK:
{
/* release interrupt flag */
TWCR |= (1 << TWINT);
/* change state machine */
twi.sm = DATA_RECEIVE;
break;
}
case TW_MR_SLA_NACK:
{
/* if no acknowledge than force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
/* change state machine to error state */
twi.sm = TRANSMISSION_ERROR;
break;
}
case TW_MR_DATA_ACK:
{
/* if acknowledge than force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
/* change state machine to error state */
twi.sm = TRANSMISSION_ERROR;
break;
}
case TW_MR_DATA_NACK:
{
/* transfer complete - move data under the pointer */
*(twi.buffer[twi.tail].receive_value_ptr) = TWDR;
/* move the tail forward */
twi.tail = ((twi.tail + 1) & (TWI_BUFFER_SIZE - 1));
/* check if we have another byte to send / receive */
if (twi.head != twi.tail)
{
/* force repeated start and release interrupt flag */
TWCR |= (1 << TWSTA) | (1 << TWINT);
}
else
{
/* stop bit transmit and release interrupt flag */
TWCR |= (1 << TWSTO) | (1 << TWINT);
/* change state machine to idle */
twi.sm = IDLE;
}
break;
}
default:
{
break;
}
}
}
/* timer overflow interrupt service routine */
ISR(TIMER0_OVF_vect)
{
}
void TimerInit()
{
/* overflow interrupt enable and start timer on frequency 8MHz / 1024 */
TIMSK |= (1 << TOIE0);
TCCR0 |= (1 << CS00) | (1 << CS02);
}
void TwiInit()
{
/* 100 kHz frequency of TWI module */
TWBR = 32;
/* enable TWI module and interrupt from them */
TWCR = (1 << TWEN) | (1 << TWIE);
}
int main(void)
{
/* display initialization */
lcd_init();
/* TWI structure initialization */
twi.sm = IDLE;
twi.head = 0;
twi.tail = 0;
/* diodes output */
DDRB |= (1 << PB0);
DDRD |= (1 << PD7);
/* diodes turn off */
PORTB |= (1 << PB0);
PORTD |= (1 << PD7);
/* pull up resistor on PD5 input switch */
PORTD |= (1 << PD5);
TimerInit();
TwiInit();
sei();
/* put interesting commands into TWI buffer */
twi_receive(WHO_AM_I, &data);
twi_receive(WHO_AM_I, &data_retry);
while (1)
{
/* if switch pressed */
if (!((PIND >> PD5) & 1))
{
if (data == 0x68)
{
PORTB &= ~(1 << PB0);
}
else
{
PORTB |= (1 << PB0);
}
}
}
}
i wiecie co, po kliknięciu przycisku podłączonego do PD5 w pierwszym przypadku dioda się nie zapala (wyzwalana stanem niskim) a w drugim się zapala. Patrząc jednak na oscyloskopie na SDA i SCL to w obydwu przypadkach maszyna stanów poprawnie przechodzi przez wszystkie stany i generuje odpowiednie poprawne przebiegi protokołu. Ktoś może powiedzieć co dokładnie się stało że to zaczęło działać? PS. Kod jest prawie ten sam, dodałem w trzech miejscach tylko volatile
. Przy definicji data
, przy argumencie funkcji twi_receive
i w deklaracji pola wskaźnikowego struktury, możecie użyć beyond compare
do porównania.