/* * * pyrometer.c ... firmware for a contactless thermometer based on atmega MCU and MLX90614 sensor * * * * Copyright (C) 2012 Jiri Pittner * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * * * */ //floating point needs LIB=-lm -Wl,-u,vfprintf -Wl,-u,vfscanf -lprintf_flt -lscanf_flt //NOTICE: the MLX90614 sensor does not comply fully with I2C or SMBUS specs, since there is //a strict timeout - it stops communication when SCL stays high for 50us after the start condition //This is not an issue here but e.g. access via libmpsse and ft2232h from a PC will nto work /*PIN assignment MCU = ATmega32 14.7456MHz LCD display (HD44780): PA0-3=DB4-7 PA4=E PA5=RW PA6=RS PA7=light (switched via transistor) standard i2c: PC0=SCL pullup 3k3 PC1=SDA pullup 3k3 RX - pulldown about 10k to prevent spurious input RESET - pullup */ #undef DO_EMISIVITY //writing emisivity did not work //address of emisivity register in MLX #define EMISIVITY 0x24 #define BAUD 115200 #include "backward.h" #include #include #include #include #include #include #include #include #include #if defined(at90s2313) || defined(at90s8535) #else #define ATmega #endif #ifdef ATmega #define USR UCSRA #endif #if (XTAL == 14745600L) #define oversampling 4 #else #define oversampling 2 #endif #ifndef MCU #define emulate #else #define AVR_assembler #endif #define itoa10(N,S) itoa(N,S,10) #define itoa16(N,S) itoa(N,S,16) void printP (PGM_P string){ char c; c=pgm_read_byte(string); while (c) { loop_until_bit_is_set(USR, UDRE); UDR = c; c=pgm_read_byte(++string); } return; } void print (char *string){ while (*string) { loop_until_bit_is_set(USR, UDRE); UDR = *string++; } return; } //UART initialize #ifdef ATmega #define UCR UCSRB #define UART_INIT(baud) { \ UBRRH=0; \ UBRRL= (XTAL/baud+15)/16-1; \ UCSRB=(1<>4); lcd_w4bit(rs,x); } void lcd_clear(void) { lcd_wbyte(0,0x01); delay_xs(30000); } void lcd_init(void) { lcd_init4bit(); lcd_wbyte(0,0x28); lcd_wbyte(0,0x08); lcd_clear(); lcd_wbyte(0,0x06); lcd_wbyte(0,0x0c); } void lcd_print(char *t) { while(*t) if(*t=='\n') {++t; lcd_wbyte(0,0xc0);} else lcd_wbyte(1,*t++); } void lcd_print8(char *t) { uint8_t l=0; while(*t) {lcd_wbyte(1,*t++); ++l;} while(l<8) {lcd_wbyte(1,' '); ++l;} } void lcd_cursor(uint8_t r, uint8_t c) { lcd_wbyte(0,0x80+c+(r<<6)); } //i2c routines void I2C_Init(void) { cbi(DDRC,PC0); cbi(DDRC,PC1); #if defined(TWPS0) /* has prescaler (mega128 & newer) */ TWSR = 0; #endif TWBR = (XTAL/100000UL - 16) / 2; sbi(TWCR,TWEN); } void MLX_i2c_init(void) { //switch from the PWM to I2C mode cbi(TWCR,TWEN); cbi(DDRC,PC1); sbi(DDRC,PC0); cbi(PORTC,PC0); delay_ms(5); sbi(PORTC,PC0); delay_ms(1); cbi(DDRC,PC0); } void I2C_Read (uint8_t *x) { TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA); while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ *x = TWDR; } void I2C_Write(uint8_t x) { TWDR = x; TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */ while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ } #define TW_WRITE 0 #define TW_READ 1 void calcpec(uint8_t *pec, uint8_t data) { data ^= *pec; uint8_t i; for(i=0; i<8; ++i) { if(data&0x80) { data <<= 1; data ^= 0x07; } else data <<= 1; } *pec = data; } uint16_t MLX_read_word(uint8_t sla, uint8_t cmd) { uint8_t pec=0; TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* send start condition */ while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ /* send SLA+W */ TWDR = sla | TW_WRITE; TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */ calcpec(&pec, sla | TW_WRITE); while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ //send command I2C_Write(cmd); TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* send start condition */ calcpec(&pec,cmd); while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ /* send SLA+R */ TWDR = sla | TW_READ; TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */ calcpec(&pec, sla | TW_READ); while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ uint8_t l,h,pec0; I2C_Read(&l); calcpec(&pec,l); I2C_Read(&h); calcpec(&pec,h); I2C_Read(&pec0); //stop TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); /* send stop condition */ //check PEC if(pec!=pec0) return 0x8000; return l|((uint16_t)h<<8); } void MLX_write_word(uint8_t sa, uint8_t cmd, uint16_t w) { uint8_t pec=0; TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* send start condition */ while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ /* send SLA+W */ TWDR = sa | TW_WRITE; TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */ calcpec(&pec, sa | TW_WRITE); while ((TWCR & _BV(TWINT)) == 0) ; /* wait for transmission */ //send command I2C_Write(cmd); calcpec(&pec,cmd); uint8_t l,h; l=w&0xff; h=w>>8; I2C_Write(l); calcpec(&pec,l); I2C_Write(h); calcpec(&pec,h); I2C_Write(pec); { char c[8]; printP(PSTR("PEC ")); itoa10(pec,c); print(c); printP(PSTR("\n")); } //stop TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); /* send stop condition */ } void MLX_setup(void) { //factory default seems OK } static volatile uint8_t menumode; static volatile double emisivity; void store_emisivity(float e) { uint16_t ee = 65535.*e; char c[8]; printP(PSTR("test ")); itoa10(ee,c); print(c); printP(PSTR("\n")); MLX_write_word(0,EMISIVITY,ee); } float read_emisivity(void) { uint16_t emis=MLX_read_word(0,EMISIVITY); float e = emis; e /= 65535.; return e; } void process_press(uint8_t length) { if(length) { menumode ^= 1; if(menumode==0) store_emisivity(emisivity); else { //refresh emisivity emisivity =read_emisivity(); lcd_clear(); } } else switch(menumode) { case 0: light ^= (1<5000); } INTERRUPT(SIG_OVERFLOW0) { ++timer0x; } int main(void) { UART_INIT(BAUD); light=0; //global interrupt activate sbi(SREG,7); uint8_t printing=0; //write initial message to the display and blink leds printP(PSTR("Reset!\n")); lcd_init(); lcd_print("Reset!"); MLX_i2c_init(); print("pyrometer switched to i2c\n"); I2C_Init(); print("i2c initialized\n"); MLX_setup(); //start timer0 TCNT0=0; TCCR0=CK1024; OCR0=0; sbi(TIMSK,TOIE0); delay_ms(100); MLX_read_word(0,6); MLX_read_word(0,7); #ifdef DO_EMISIVITY uint16_t emis=MLX_read_word(0,EMISIVITY); emisivity = emis+1.; emisivity /= 65536.; {char c[32]; sprintf(c,"E=%6.2f\n",emisivity); print(c); lcd_clear(); lcd_print(c); } delay_ms(2000); #endif //button cbi(DDRD,PD2); sbi(PORTD,PD2);//pullup delay_ms(100); //prevent false impulse after power up sbi(MCUCR,ISC00); cbi(MCUCR,ISC01); //activate both edges sbi(GIMSK,INT0); menumode=0; //LOOP while(1) { uint16_t ta,t1; uint8_t i; uint32_t tas,t1s; tas=t1s=0; #define NREP 4 for(i=0; i<(1<>NREP; t1 = t1s >> NREP; { char c[32]; switch(menumode) { case 0: sprintf(c,"O=%6.2f\nA=%6.2f\n",.02*t1-273.15, .02*ta-273.15); break; case 1: sprintf(c,"E=%6.2f\n",emisivity); break; } if(printing) print(c); lcd_cursor(0,0); lcd_print(c); } //check incoming commands from uart if(uartline) { switch(inuart[0]) { case 'o': printing ^= 1; break; case 'R': //reset watchdog(1);while(1); break; case 'E': //set emisivity { float e; if(1==sscanf(inuart+2,"%f",&e)) { if(e>1. || e<0.) break; emisivity=e; store_emisivity(emisivity); printP(PSTR("Emisivity set to ")); print(inuart+2); printP(PSTR("\n")); } } break; case 'e': //check emisivity {char c[32]; emisivity = read_emisivity(); sprintf(c,"E=%6.2f\n",emisivity); print(c); } break; case 0: //empty string break; default: printP(PSTR("Unknown command: ")); print(inuart); printP(PSTR("\n")); break; } uartline=inuartlen=0; } delay_ms(400); }//while }