Juste 4 fils sur le clone Arduino nano : GND, VCC (3.3V), SCL(A5), SDA(A4)
Lecture assez ardue des datasheets ATmega328p (TWI aka I2C, section 21) et MLX90614 pour finalement pas grand chose : après avoir initialisé l'interface I²C, il 'suffit' d'envoyer <S><SLA+W><reg><S><SLA+R><data-in><data-in><data-in><P>. Cela se fait via le registre TWDR (data), activé par TWCR (control) en vérifiant le TWSR (status dont TW_STATUS est un subset). Avec <S> pour 'Start'; SLA pour 'SLave Address'; R/W, le sens de communication; et <P> pour 'stoP'. Toutes les activations n'utilisent pas les mêmes bits (mais TWINT & TWEN sont toujours présents); TWINT marque également la fin d'exécution de la commande (cela devrait se faire par interruption mais ici, on se contente de 'poller' le bit). Chaque phase présente un TWSR/TW_STATUS différent pour indiquer que l'opération s'est bien déroulée. Le composant renvoie 3 octets : deux octets pour la température (en 1/50-ièmes de degré Kelvin, LSB first) et un CRC non vérifié ici (PEC=Packet Error Code).
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <util/twi.h>
#include <stdio.h>
#include <string.h>
/*
** UART - stdio
*/
int uart_getchar(FILE *stream)
{
while (!(UCSR0A & (1<<RXC0)))
;
return(UDR0);
}
int uart_putchar(char c, FILE *stream)
{
while (!(UCSR0A & (1<<UDRE0)))
;
UDR0 = c;
if (c == '\n')
uart_putchar('\r', stream);
}
void uart_init(void)
{
UBRR0H = 0x00;
UBRR0L = 103; /* 9600 bps | F_CPU/16/baud-1 */
UCSR0C = (1<<USBS0) | (3<<UCSZ00); /* 8N1 */
UCSR0B = (1<<RXEN0) | (1<<TXEN0);
fdevopen(uart_putchar, uart_getchar); /* stdio init */
}
/*-----------------------------------------------*/
/*
** TWI - I2C (single master)
** SCL A5 PC5
** SDA A4 PC4
*/
int twi_stop();
void twi_init()
{
TWSR = 0; /* prescaler = 0 */
TWBR = (F_CPU / 100000UL - 16) / 2; /* 0x48 -> 100 kHz */
twi_stop();
}
#define twi_wait() { while ((TWCR & _BV(TWINT)) == 0) ; }
#define twi_go(x) TWCR = _BV(TWINT)|_BV(TWEN)|x
int twi_start() /* send start condition */
{
twi_go(_BV(TWSTA)); /* start */
twi_wait();
if (TW_STATUS != TW_START && TW_STATUS != TW_REP_START)
{
fprintf(stderr, "**** I2C/start : unexpected status 0x%02x\n", TW_STATUS);
return(-1);
}
return(0);
}
int twi_stop()
{
twi_go(_BV(TWSTO)); /* stop */
return(-1); /* always */
}
int twi_sla(uint8_t sla, uint8_t rw)
{
TWDR = (sla<<1) | rw;
twi_go(0);
twi_wait();
if ((rw == TW_WRITE && TW_STATUS != TW_MT_SLA_ACK)
|| (rw == TW_READ && TW_STATUS != TW_MR_SLA_ACK))
{
fprintf(stderr, "**** I2C/slave address : unexpected status 0x%02x\n", TW_STATUS);
return(-1);
}
return(0);
}
int twi_wcmd(uint8_t data)
{
TWDR = data; // reg
twi_go(0);
twi_wait();
if (TW_STATUS != TW_MT_DATA_ACK)
{
fprintf(stderr, "**** twi_wcmd : unexpected status 0x%02x\n", TW_STATUS);
return(-1);
}
return(0);
}
int twi_rdata()
{
twi_go(_BV(TWEA));
twi_wait();
if (TW_STATUS != TW_MR_DATA_ACK)
{
fprintf(stderr, "**** twi_rdata : unexpected status 0x%02x\n", TW_STATUS);
return(-1);
}
return(TWDR);
}
pr_temp(long t)
{
char s;
int a, b;
t -= 27350; /* Kelvin * 100 -> Celsius * 100 */
s = (t < 0) ? '-' : '+';
if (t < 0)
t = -t;
a = t / 100; b = t % 100;
printf("%c%d.%02d", s, a, b);
}
#define MLX_ADD 0x5A /* MLX90614 SLA (i2c default SLave Address) */
#define CMD_Ta 0x06 /* ambiant temperature */
#define CMD_Tobj 0x07 /* IR object temperature */
long twi_MLX_get100tc(uint8_t sla, uint8_t reg)
{
int a, b;
long t;
if (twi_start() < 0 || twi_sla(sla, TW_WRITE) < 0)
return(twi_stop());
if (twi_wcmd(reg) < 0)
return(twi_stop());
if (twi_start() < 0 || twi_sla(sla, TW_READ) < 0)
return(twi_stop());
if ((a = twi_rdata()) < 0)
return(twi_stop());
if ((b = twi_rdata()) < 0)
return(twi_stop());
twi_rdata(); /* PEC - don't care */
twi_stop();
t = (a + (b << 8));
return(t*2); /* Kelvin * 100 */
}
/*-----------------------------------------------*/
int main(void)
{
uart_init();
twi_init();
for(;;)
{
printf("\nT ambiant : ");
pr_temp(twi_MLX_get100tc(MLX_ADD, CMD_Ta));
printf(", T obj : ");
pr_temp(twi_MLX_get100tc(MLX_ADD, CMD_Tobj));
_delay_ms(500L);
}
}
Mais, bien sûr, on pourrait utiliser la lib Wire de l'Arduino...
Pour la compilation, le téléchargement et le test :
$ avr-gcc -mmcu=atmega328 -Os -o gy906.elf gy906.c $ avrdude -c arduino -p atmega328P -P /dev/ttyUSB0 -b 57600 -U flash:w:gy906.elf $ minicom -D /dev/ttyUSB0 -b 9600
