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