| Author |
Message |
|
|
Posted: Nov 09, 2009 - 12:38 PM |
|

Joined: Nov 03, 2009
Posts: 6
|
|
Hello.
I'm working with Atmega16. My aim is to read temperature data from LM75. Source which I use to interface i2c and lm75 is standart free downloaded from internet. Code is here:
http://www.vabolis.lt/stuff/20070919.zip
(http://www.vabolis.lt/2007/09/19/atmega-usb-005/)
Bellow is main code:
It works only when sensors_init() function is disabled. LCD shows results. So that i2c interface is not initialized.
Atmega16 use PC0 and PC1 for i2c interface. So, I have changed pull-up'ing ports in i2c.c
from PORTC |= (1<<5)|(1<<4); to PORTC |= (1<<1)|(1<<0); But it's dont matter because pull-up is external with 10K resistors. Clock is internal 1MHz.
Do you have idea whats wrong? Why I cannot read data?
Best Regards,
Vytautas
Code:
/*************************************************************************
Title: I2C FM75 irenginys ir LCD
Author: Savel
File:
Software: AVR-GCC 3.3
Hardware: ATMEGA16 testine plokste
**************************************************************************/
#define F_CPU 12000000UL
#define LM75_ADDR (LM75_ADDR_BASE + 0) //pasitikrinti
#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "lcd.h"
#include <util\delay.h>
#include "i2c.c"
#include "lm75.c"
int sensors_init(void)
{
i2c_init();
return lm75_configure(LM75_ADDR, 0x60); //cia komanda FM75, daugiau tikslumo. Skaityti FM75/LM75 datasheeta.
}
int main(void)
{
char buffer[16];
unsigned char dst[2];
int x=0;
sensors_init();
/* initialize display, cursor off */
lcd_init(LCD_DISP_ON);
/* turn off cursor */
lcd_command(LCD_DISP_ON);
lcd_clrscr();
lcd_puts("FM75 sensorius");
for (;;) { /* loop forever */
dst[0]=0; dst[1]=0;
// temperaturos nuskaitymas is FM75 cipo
lcd_gotoxy(0,1);
x = lm75_readRegister(LM75_ADDR, LM75_REG_TEMP, &dst[0], 2);
if (x==0){
x=dst[0];
itoa( x, buffer, 10);
lcd_puts("temp: ");
lcd_gotoxy(5,1);
lcd_puts(buffer);
lcd_puts(".");
x=dst[1]; // Cia skaiciuojam po kablelio. 1/16 laipsnio.
if (x<1) {lcd_puts("0");}
if (x<17) {lcd_puts("0");}
x=dst[1]*4;
itoa( x, buffer, 10);
lcd_puts(buffer);
} else {lcd_puts("Sensor error "); }
for (x=0;x<10;x++) { _delay_ms(20); }
}
}
|
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 03:57 AM |
|


Joined: Jan 14, 2008
Posts: 1147
Location: San Diego
|
|
|
|
|
|
|
Posted: Nov 10, 2009 - 04:16 AM |
|


Joined: Mar 28, 2001
Posts: 20339
Location: Sydney, Australia (Gum trees, Koalas and Kangaroos, No Edelweiss)
|
|
|
Quote:
Clock is internal 1MHz.
Code:
#define F_CPU 12000000UL
Which is it? |
_________________ John Samperi
Ampertronics Pty. Ltd.
www.ampertronics.com.au
* Electronic Design * Custom Products * Contract Assembly
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 08:00 AM |
|

Joined: Nov 03, 2009
Posts: 6
|
|
I have changed to this:
#define F_CPU 1000000UL
Maybe TWI clock is too slow now? 1MHz/(4+2*255)~2KHz |
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 08:04 AM |
|

Joined: Nov 03, 2009
Posts: 6
|
|
sensors_init() function refers to another functions:
Code:
int sensors_init(void)
{
i2c_init();
return lm75_configure(LM75_ADDR, 0x60); //cia komanda FM75, daugiau tikslumo. Skaityti FM75/LM75 datasheeta.
}
lm75.c file
Code:
#include <string.h>
#include "i2c.h"
#include "lm75.h"
int lm75_writeRegister(char i2c_addr, char reg_addr, unsigned char *dat, char len)
{
unsigned char tmp[len+1];
tmp[0] = reg_addr & 0x03;
memcpy(&tmp[1], dat, len);
return i2c_transaction(i2c_addr, len + 1, tmp, 0, NULL);
}
int lm75_readRegister(char i2c_addr, char reg_addr, unsigned char *dst, char len)
{
unsigned char tmp;
tmp = reg_addr & 0x03;
return i2c_transaction(i2c_addr, 1, &tmp, len, dst);
}
int lm75_configure(char i2c_addr, unsigned char cfg)
{
return lm75_writeRegister(i2c_addr, LM75_REG_CFG, &cfg, 1);
}
and these functions refers to i2c.c files functions:
Code:
#include <avr/io.h>
#include <util/twi.h>
#include "i2c.h"
void i2c_init(void)
{
/* Use internal pullups */
PORTC |= (1<<5)|(1<<4);
/* This gives roughly 30 khz with a 16mhz xtal */
TWBR = 255;
TWSR &= ~((1<<TWPS1)|(1<<TWPS0));
}
int i2c_transaction(unsigned char addr, int wr_len, unsigned char *wr_data,
int rd_len, unsigned char *rd_data)
{
int ret =0;
if (wr_len==0 && rd_len==0)
return -1;
if (wr_len != 0)
{
// Send a start condition
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)))
{ /* do nothing */ }
if ((TWSR & 0xF8) != TW_START)
return 1; /* Failed */
TWDR = (addr<<1) | 0; /* Address + write(0) */
TWCR = (1<<TWINT)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)))
{ /* do nothing */ }
/* TWSR can be:
* TW_MT_SLA_ACK, TW_MT_SLA_NACK or TW_MR_ARB_LOST */
if ((TWSR & 0xF8) != TW_MT_SLA_ACK) {
ret = 2;
goto err;
}
while (wr_len--)
{
TWDR = *wr_data;
TWCR = (1<<TWINT)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)))
{ /* do nothing */ }
wr_data++;
}
} // if (wr_len != 0)
if (rd_len != 0)
{
/* Do a repeated start condition */
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)))
{ /* do nothing */ }
if (TWSR != TW_REP_START) {
ret = 3;
goto err;
}
TWDR = (addr<<1) | 1; /* Address + read(1) */
TWCR = (1<<TWINT)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)))
{ /* do nothing */ }
/* TWSR can be:
* TW_MR_SLA_ACK, TW_MR_SLA_NACK or TW_MR_ARB_LOST */
if (TWSR != TW_MR_SLA_ACK) {
ret = 4;
goto err;
}
while (rd_len--)
{
if (rd_len)
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
else
TWCR = (1<<TWINT)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)))
{ /* do nothing */ }
*rd_data = TWDR;
rd_data++;
}
} // if (rd_len != 0)
TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
return 0;
err:
TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
return ret;
}
|
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 08:15 AM |
|


Joined: Jan 14, 2008
Posts: 1147
Location: San Diego
|
|
|
Quote:
sensors_init() function refers to another functions:
Yes exactly, and that function should return a value which will give you a good idea of what the problem is. |
_________________ ~~John
TWI C source code
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 08:32 AM |
|

Joined: Feb 12, 2005
Posts: 16280
Location: Wormshill, England
|
|
From memory, the LM75 returns the degrees in dst[0] and the 0.5 degree in dst[1]. So you only need:
Code:
atoi(dst[0], buf, 10);
lcd_puts(buf);
if (dst[1] != 0) lcd_puts(".5");
I also note that you have TWBR = 255, which will produce a massively slow bus speed. With 1MHz clock, you need TWBR = 1 or possibly up to 10.
Your 10k pull-ups should be fine unless you have a very large capacitance on the bus, You could reduce to 4k7 or 2k2 if necessary.
You should always initialise the TWI. So try wth a sensible TWBR.
David. |
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 09:07 AM |
|


Joined: Mar 28, 2001
Posts: 20339
Location: Sydney, Australia (Gum trees, Koalas and Kangaroos, No Edelweiss)
|
|
|
Quote:
you need TWBR = 1 or possibly up to 10.
From the data sheet
Quote:
Note: TWBR should be 10 or higher if the TWI operates in Master mode. If TWBR is lower than
10, the Master may produce an incorrect output on SDA and SCL for the reminder of the
byte. The problem occurs when operating the TWI in Master mode, sending Start + SLA
+ R/W to a Slave (a Slave does not need to be connected to the bus for the condition to
happen).
|
_________________ John Samperi
Ampertronics Pty. Ltd.
www.ampertronics.com.au
* Electronic Design * Custom Products * Contract Assembly
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 09:19 AM |
|

Joined: Feb 12, 2005
Posts: 16280
Location: Wormshill, England
|
|
Yes, John this is on the Mega16 and Mega32 data sheet.
I have never discovered this behaviour on a real chip. It is is not in other AVR families data sheets.
David. |
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 01:51 PM |
|


Joined: May 30, 2002
Posts: 652
Location: Landgraaf, The Netherlands
|
|
Hi,
I see in your code :
Code:
#define LM75_ADDR (LM75_ADDR_BASE + 0)
Did you #define LM_ADDR_BASE somewhere ? Its probably 0x90 or 0x92. |
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 04:45 PM |
|

Joined: Nov 03, 2009
Posts: 6
|
|
I have done several changes:
my board with Atmega16 has external 16MHz oscilator. So, in lcd_test.c I have changed:
Code:
#define F_CPU 16000000UL
and in lcd.h:
Code:
#define XTAL 16000000UL
Base LM75 address is defined in lm75.h file:
Code:
#define LM75_ADDR_BASE 0x48
The main file takes this constant and use like this:
Code:
#define LM75_ADDR (LM75_ADDR_BASE + 0x48)
3 address pins of LM75 are connected to GND.
In PonyProg I have deactivated these fuse bits:
CKOPT, BODLEVEL, BODEN, CKSEL0 |
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 05:10 PM |
|

Joined: Feb 12, 2005
Posts: 16280
Location: Wormshill, England
|
|
I am surprised anything will work at all!
Your
int i2c_transaction(unsigned char addr, int wr_len, unsigned char *wr_data, int rd_len, unsigned char *rd_data);
requires a 7-bit Slave address, which should be 0x48 for your LM75.
Now you are very sensibly using the error-return from your lm75() functions. I would personally name the variable something better than 'x'. And now that you have the error status, why not examine its value?
I have no idea what your PonyProg fuses mean by 'deactivated'. The BODxx "brown-out" fuses will only affect your AVR if you are running on a flat battery.
If your AVR is running at 1MHz, you should not write F_CPU as 16MHz. You should also set TWBR to a sensible value.
David. |
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2009 - 05:14 PM |
|

Joined: Nov 03, 2009
Posts: 6
|
|
I think that this code forever in this line of i2c.c file:
Code:
if (wr_len != 0)
{
// Send a start condition
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)))
{ DDRD=255;PORTD=255;/* do nothing */ }
PORTD=0b00000000; //stops!!!!!
hight interupt signal stops any action. (PORTD Leds light forever |
|
|
| |
|
|
|
|
|
Posted: Nov 11, 2009 - 10:52 AM |
|

Joined: Nov 03, 2009
Posts: 6
|
|
I'm using 16Mhz external clock.
I think that problem is related to hardware. So, I'm going to buy new LM75 and create new i2c device. |
|
|
| |
|
|
|
|
|
Posted: Nov 11, 2009 - 06:06 PM |
|

Joined: Feb 12, 2005
Posts: 16280
Location: Wormshill, England
|
|
I do not have a LM75, but used your program to write to a CONFIG register of a PCF8563 RTC. And then read the time back.
Your code works ok with a correctly addressed Slave device.
If you have a device on the bus but not the correct address, you ge "Sensor error"
If you have nothing on the bus, the program just HANGS.
It does not seem to matter if you have a large TWBR. But it certainly matters if you use wrong Slave address. It should be 0x48 for the LM75.
Chips do not normally die, unless you have deliberately fried them. So I would check your Slave address first and then your wiring.
David. |
|
|
| |
|
|
|
|
|