搜索
bottom↓
回复: 20

AVR利用TWI访问LM75程序

[复制链接]

出0入0汤圆

发表于 2005-11-21 09:02:29 | 显示全部楼层 |阅读模式
前几天在duoqian大虾的指导下,把AVR利用TWI访问LM75的程序搞定了,现在把程序贴出来与大家一起分享。

#include "iom128v.h"

#include "macros.h"



#define uchar unsigned char

#define uint unsigned int

#define CLRBIT(ADDRESS,BIT) (ADDRESS &= ~(1 << BIT))

#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1 << BIT))

#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1 << BIT))

#define XORBIT(ADDRESS,BIT) (ADDRESS ^= (1 << BIT))



#define TempRegAddr 0x00   //Temp的寄存器地址

#define ReadLM75Addr 0x91  //0b10010001,LM75的地址格式是0b1001A2A1A0W/R,这里设备地址为0,读操作

#define WriteLM75Addr 0x90 //写操作

#define twi_stop() TWCR=0x94                                                         

#define twi_start() TWCR=0xa4                                                                                           

#define check_TWINT() while(!(TWCR & (1<<TWINT)))  //轮询等待TEINT置位



#define br2400   0

#define br4800   1

#define br9600   2

#define br14400  3

#define br19200  4

#define br28800  5

#define br38400  6

#define br57600  7

#define br76800  8

#define br115200 9



uchar TempHigh;

uchar TempLow;

uchar TempSign,TempData;

uchar TwiStatus;



/*****************************************

              延时1ms子程序

******************************************/

void delay_1ms( void )

{

    uint k ;

    for ( k = 0 ; k < ( 8*142-2 ) ; k++ ) // 定时1ms公式:xtal*142-2

         ;  

}



/*****************************************

              延时nms子程序

******************************************/

void Delay ( uint  n )

{

    uint p ;

    for( p = 0 ; p < n ;p++ )

        {

           delay_1ms() ;

        }

}



/*****************************************

                I/O口初始化子程序

*****************************************/

void port_init(void)

{

        PORTA = 0xff;

        DDRA  = 0xff;

        PORTB = 0xff;

        DDRB  = 0xC0;

        PORTC = 0xff;

        DDRC  = 0xE1;

        PORTD = 0xff;

        DDRD  = 0xf0;

        PORTE = 0xff;

        DDRE  = 0x0C;

        PORTF = 0xff;

        DDRF  = 0x00;

        PORTG = 0xff;

        DDRG  = 0x03;

}





/*****************************************

             状态灯控制子程序

******************************************/

void StatusLihgting(uchar status)

{

    if(status == 0)

        PORTB &=~ BIT(6);

        else

        PORTB |= BIT(6);

}



/*****************************************

             串口初始化子程序

******************************************/

void InitUart0(uchar baudrate)

{

    UCSR0B = 0x00;                                                                

    UCSR0A = 0x00;

    UCSR0C = 0x06;                          //字符长度为8bits

    switch(baudrate)

    {

      case 0: //2400----2398 (0.1%)

         UBRR0L = 0xA0;

               UBRR0H = 0x00;

                   break;

          case 1: //4800----4808 (0.2%)

         UBRR0L = 0xCF;

               UBRR0H = 0x00;

                   break;

          case 2: //9600----9615 (0.2%)

               UBRR0L = 0x67;

               UBRR0H = 0x00;

                   break;

          case 3: //14400----14493 (0.6%)

               UBRR0L = 0x44;

               UBRR0H = 0x00;

                   break;

          case 4: //19200----19231 (0.2%)

         UBRR0L = 0x33;

               UBRR0H = 0x00;        

                   break;

          case 5: //28800----28571 (0.8%)

         UBRR0L = 0x22;

               UBRR0H = 0x00;        

                   break;

          case 6: //38400----38462 (0.2%)

         UBRR0L = 0x19;

               UBRR0H = 0x00;

                   break;

          case 7: //57600----58824 (2.1%)

         UBRR0L = 0x10;

               UBRR0H = 0x00;

                   break;

          case 8: //76800-----76923 (0.2%)

               UBRR0L = 0x0C;

         UBRR0H = 0x00;

                   break;

          case 9: //115200-----111111 (3.7%)

         UBRR0L = 0x08;

         UBRR0H = 0x00;

                   break;

          default:

         UBRR0L = 0x67;

               UBRR0H = 0x00;        

                   break;

    }

    UCSR0B = 0x08;

}



/*****************************************

              串口发送子程序

******************************************/

void Send_function(uchar senddata)

{

    UDR0=senddata;

    while(!(UCSR0A & 0x40));        //等待发送完

    UCSR0A |= 0x40;                //清“发送完”标志位

}



/*****************************************

              TWI初始化子程序

            100KHZ的速率

*****************************************/

void twi_init(void)

{

    TWCR= 0X00; //disable twi

    TWBR= 0x12; //set bit rate

    TWSR= 0x01; //set prescale

    TWCR= 0x04; //enable twi

}



void init_devices(void)                       

{

    CLI();

    port_init();

    InitUart0(br9600);

    twi_init();

    SEI();

}



/*****************************************

              计算温度值子程序

LM75的TEMP寄存器的D15~D7位用来存放温度值,

该值以二进制补玛形式表现,即D15=0时,值为

正;D15=1时,值为负。具体的数值由D14~D7决定                          

*****************************************/

void temp_output(void)                                 

{

    if((TempHigh & 0x80) == 0x00)         //Temp的最高位为0,设标志0x00,即温度值为正

    TempSign=0x00;

    else

    TempSign=0xff;                  //Temp的最高位为0,设标志0xff,即温度值为负

    TempHigh <<= 1;                 //将D14~D7移至一个字节中

    if((TempLow & 0x80) == 0x00)         

    TempData=TempHigh & 0xfe;

    else

    TempData=TempHigh | 0x01;

    TempData >>= 1;                  //因为TEMP的每单位值代表0.5摄氏度,所以还要除2

}





/*****************************************

          TWI主接收方式子程序

          读取LM75的TEMP寄存器的值

******************************************/



void TWITempRead(uchar ReadDeviceAddr,uchar WriteDeviceAddr,uchar RegAddr)

{

    uint j;

    Delay(20);

    while (TwiStatus != 0x08)   //检验TWSR,屏蔽预分频位   

    {

         twi_start();           //发送START信号

         Delay(1);

         TwiStatus=TWSR & 0xF8;

   }

   //StatusLihgting(0);                                                    

    while (TwiStatus != 0x18)         //检验TWSR,SLA+W已发出,并收到ACK

    {

         TWDR = WriteDeviceAddr;//设备地址(写)

         TWCR=0x84;

         Delay(1);

         TwiStatus=TWSR & 0xF8;  

   }                                          

   while (TwiStatus != 0x28)        //DATA已发出,并收到ACK                           

   {

         TWDR = RegAddr;         //写入LM75的TEMP寄存器的地址                

         TWCR=0x84;  

         Delay(1);

         TwiStatus=TWSR & 0xF8;

   }

          

   while (TwiStatus != 0x10)         //REPEATED START信号已发出

   {

         twi_start();         //发REPEATED START信号

         Delay(1);

         TwiStatus=TWSR & 0xF8;

   }

   

   while (TwiStatus != 0x40)         //检验TWSR,SLA+R已发出,并收到ACK

   {

         TWDR =ReadDeviceAddr;         //设备地址(读)

         TWCR=0x84;  

         Delay(1);

         TwiStatus=TWSR & 0xF8;

   }

   TwiStatus=0x00;

   while (TwiStatus != 0x50)         //DATA已收到,ACK已发出

   {

         TWCR=0xc4;                  //接收高8位数据,发送ACK

         Delay(20);

         TempHigh=TWDR;       

         TwiStatus=TWSR & 0xF8;

   }

   while(TwiStatus != 0x58)    //DATA已收到,nACK已发出                  

   {

         TwiStatus=TWSR & 0xF8;

         TWCR=0x84;                 //接收低8位数据,发送nACK

         Delay(10);

         TempLow=TWDR;

   }

   twi_stop();                   //发送STOP 信号                                          

}



void main(void)

{

   init_devices();

   while(1)

   {

         Delay(2000);

         TWITempRead(ReadLM75Addr,WriteLM75Addr,TempRegAddr);

         temp_output();

         PORTE |= BIT(2);        //485发送使能

         Send_function(TempSign);

         Send_function(TempData);

   }

}
-----此内容被LOVEMCU于2005-11-22,08:57:12编辑过

出0入0汤圆

发表于 2005-11-21 15:32:09 | 显示全部楼层

出0入0汤圆

发表于 2006-3-9 13:04:33 | 显示全部楼层
顶!!!!!!!!!!!!!

出0入0汤圆

发表于 2006-4-8 19:34:56 | 显示全部楼层
我在用 twi写24呢?不知从哪开始?本人刚刚接触avr  请大家多点指教

出0入0汤圆

发表于 2006-4-8 20:05:09 | 显示全部楼层
我在用 twi写24呢?不知从哪开始?本人刚刚接触avr  请大家多点指教

出0入0汤圆

发表于 2006-5-17 14:01:07 | 显示全部楼层
我也正要用ATmel 128控制AD7416的温度输出,不知是否大同小异

出0入0汤圆

发表于 2006-9-14 14:05:22 | 显示全部楼层
新手问下,/*I主接收方式子程序,读取LM75的TEMP寄存器的值 */这段程序中的while (TwiStatus != 0x08)   //检验TWSR,屏蔽预分频位   等几个while()语句中的0x08,0x18等是代表什么怎么得出来得??另外,TwiStatus的初值是0吧?那这段程序岂不是从从上向下一直执行??不是很明白,那位高手指点一下??谢谢

出0入0汤圆

发表于 2007-9-5 14:56:32 | 显示全部楼层
while (TwiStatus != 0x18)         //检验TWSR,SLA+W已发出,并收到ACK
    {
         TWDR = WriteDeviceAddr;//设备地址(写)  
         TWCR=0x84;
         Delay(1);
         TwiStatus=TWSR & 0xF8;   
   }                                            
   while (TwiStatus != 0x28)        //DATA已发出,并收到ACK                             
   {
         TWDR = RegAddr;         //写入LM75的TEMP寄存器的地址                  
         TWCR=0x84;   
         Delay(1);
         TwiStatus=TWSR & 0xF8;  
   }

是主机发送模式的,是不是可以去掉呢?

出0入0汤圆

发表于 2007-10-30 20:40:47 | 显示全部楼层
大家好,我是新手,我想问下各位高手一个问题:我用I2C和PCF8563日历时钟芯片通信,想设定一下时间然后把时间再读出来,我用的是ATMEGA128芯片,程序是用GCCAVR编写的,我想包含头文件include <i2c.h>编译时总是出错,难道不是这个头文件吗?我的程序好象也有问题,我不加那个头文件时,编译是通过了,可是上板子就是写不进去时间,我的程序如下,哪位热心人帮忙看下,小女子感激万分!焦急等待回复中!
#include <avr/io.h>
#include <stdio.h>
#include <string.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <stdlib.h>
#define uchar unsigned char
uchar g_aSendBuf[LEN]; //读出的数据存在此缓冲区
#define LEN 6
#include "i2c.h"
#include <avr/delay.h>
#define DeviceAddr 0xA2
void delayus(unsigned char i)
{
while(i)
i--;
}

/*I2C总线主机模式错误处理*/
void error(unsigned char type)
{
switch (type & 0xF8)
  {case 0x20:     /*址址写失败*/  
   /*stop 停止*/   
     TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);  
     break;
   case 0x30:     /*数据写失败*/  
    /*stop 停止*/   
     TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);  
     break;  
   case 0x38:     /*仲裁失败*/   
     break;  
   case 0x48:     /*址址读失败*/  
    /*stop 停止*/  
         TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
         break;
   }
}


void f_I2cStart(void)
{
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if ((TWSR & 0xF8) != 0x08)
{  
error(TWSR);
}
}


void f_I2cStop(void)
{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
}


void f_I2cWriteAdd_W(unsigned char uc_I2CAdd)
{
TWDR=(uc_I2CAdd);
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if ((TWSR & 0xF8) != 0x18)
{  
error(TWSR);
}
}

void f_I2cWriteData(unsigned char uc_I2CData)
{
TWDR=(uc_I2CData);
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if ((TWSR & 0xF8) != 0x28)
{  
error(TWSR);  
}

}


//写单字节数据的完整程序
void f_Write(unsigned char uc_RegAddress,unsigned char uc_Wdata)
{
f_I2cStart();
f_I2cWriteAdd_W(DeviceAddr);
f_I2cWriteData(uc_RegAddress);
f_I2cWriteData(uc_Wdata);
f_I2cStop;
}

//下面和读有关
void f_I2cReStart(void)
{
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if ((TWSR & 0xF8) != 0x10);
{  
error(TWSR);
}
}



void f_I2cWriteAdd_R(unsigned char uc_I2CAdd)
{
TWDR=(uc_I2CAdd);
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if ((TWSR & 0x40) != 0x18)
{  
error(TWSR);
}
}



unsigned char f_I2cRead2(void)
{
unsigned char uc_ReturnData;
TWCR = (TWCR&0X8F)|(1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if ((TWSR & 0xF8) != 0x58)
return 0;
uc_ReturnData=TWDR;
return uc_ReturnData;
}



//读单字节数据的完整程序
unsigned char f_Read(unsigned char uc_RegAddress)
{
unsigned char uc_ReadData;
f_I2cStart();
f_I2cWriteAdd_W(DeviceAddr);
f_I2cWriteData(uc_RegAddress);

f_I2cReStart();
f_I2cWriteAdd_R(DeviceAddr+1);
uc_ReadData=f_I2cRead2();
f_I2cStop;
return uc_ReadData;
}



main( )
{
   cli();
   f_Write(8,0X98);
   f_Write(7,0X12);
   f_Write(5,0X24);
   f_Write(4,0X15);
   f_Write(3,0X59);
   f_Write(2,0X59);
    delayus(10);
    g_aSendBuf[0]=f_Read(0X08);
    g_aSendBuf[1]=f_Read(0X07);
    g_aSendBuf[2]=f_Read(0X05);
    g_aSendBuf[3]=f_Read(0X04);
    g_aSendBuf[4]=f_Read(0X03);
    g_aSendBuf[5]=f_Read(0X02);      
    delayus(10);
    sei();
}

出0入0汤圆

发表于 2007-12-18 09:54:41 | 显示全部楼层
我觉得这段程序用WHILE循环的方式来写不太对

因为如果发送一个字节数据失败他的下一步操作应当不能是重新发送这个字节的数据

如果这段代码能够通过试验,那么去掉循环也可以通过试验的,大家讨论下,谢谢

出0入0汤圆

发表于 2008-3-5 18:20:07 | 显示全部楼层
我同意飞天的意见,如果有一点差错的话,程序就会停在那里。

出0入0汤圆

发表于 2008-11-11 13:10:02 | 显示全部楼层
TWI汇编实例:

ldi&nbsp;r16,&nbsp;(1&lt;&lt;TWINT)|(1&lt;&lt;TWSTA)|(1&lt;&lt;TWEN)

out&nbsp;TWCR,&nbsp;r16

wait1:

in&nbsp;r16,TWCR

sbrs&nbsp;r16,TWINT

rjmp&nbsp;wait1

in&nbsp;r16,TWSR

andi&nbsp;r16,&nbsp;0xF8

cpi&nbsp;r16,&nbsp;START

brne&nbsp;ERROR

ldi&nbsp;r16,&nbsp;SLA_W

out&nbsp;TWDR,&nbsp;r16

ldi&nbsp;r16,&nbsp;(1&lt;&lt;TWINT)&nbsp;|&nbsp;(1&lt;&lt;TWEN)

out&nbsp;TWCR,&nbsp;r16

wait2:

in&nbsp;r16,TWCR

sbrs&nbsp;r16,TWINT

rjmp&nbsp;wait2

in&nbsp;r16,TWSR

andi&nbsp;r16,&nbsp;0xF8

cpi&nbsp;r16,&nbsp;MT_SLA_ACK

brne&nbsp;ERROR

ldi&nbsp;r16,&nbsp;DATA

out&nbsp;TWDR,&nbsp;r16

ldi&nbsp;r16,&nbsp;(1&lt;&lt;TWINT)&nbsp;|&nbsp;(1&lt;&lt;TWEN)

out&nbsp;TWCR,&nbsp;r16

wait3:

in&nbsp;r16,TWCR

sbrs&nbsp;r16,TWINT

rjmp&nbsp;wait3

in&nbsp;r16,TWSR

andi&nbsp;r16,&nbsp;0xF8

cpi&nbsp;r16,&nbsp;MT_DATA_ACK

brne&nbsp;ERROR

ldi&nbsp;r16,&nbsp;(1&lt;&lt;TWINT)|(1&lt;&lt;TWEN)|(1&lt;&lt;TWSTO)

out&nbsp;TWCR,&nbsp;r16

出0入0汤圆

发表于 2008-11-11 11:51:39 | 显示全部楼层
我不同意楼上的意见。WHILE循环是用来轮询中断的,不是用来重新发送字节数据的。这个结构我觉得是合理的。和atmega的datasheet上提供的汇编实例是一致的。

不过我发现如下8楼的sla+r的子程序有问题,你的if&nbsp;((TWSR&nbsp;&&nbsp;0x40)&nbsp;!=&nbsp;0x18)是不是应该改为if&nbsp;((TWSR&nbsp;&&nbsp;0xf8)&nbsp;!=&nbsp;0x40)?&nbsp;

void&nbsp;f_I2cWriteAdd_R(unsigned&nbsp;char&nbsp;uc_I2CAdd)&nbsp;

{&nbsp;

TWDR=(uc_I2CAdd);&nbsp;

TWCR&nbsp;=&nbsp;(1&nbsp;&lt;&lt;&nbsp;TWINT)&nbsp;|&nbsp;(1&nbsp;&lt;&lt;&nbsp;TWEN);&nbsp;

while&nbsp;(!(TWCR&nbsp;&&nbsp;(1&nbsp;&lt;&lt;&nbsp;TWINT)));&nbsp;

if&nbsp;((TWSR&nbsp;&&nbsp;0x40)&nbsp;!=&nbsp;0x18)&nbsp;

&nbsp;{&nbsp;&nbsp;&nbsp;

error(TWSR);&nbsp;&nbsp;

&nbsp;}&nbsp;

}&nbsp;

还有8楼如下的data+r子程序,语句TWCR&nbsp;=&nbsp;(TWCR&0X8F)|(1&nbsp;&lt;&lt;&nbsp;TWINT)&nbsp;|&nbsp;(1&nbsp;&lt;&lt;&nbsp;TWEN)我不太明白,为什么不直接用TWCR&nbsp;=&nbsp;(1&nbsp;&lt;&lt;&nbsp;TWINT)&nbsp;|&nbsp;(1&nbsp;&lt;&lt;&nbsp;TWEN)?&nbsp;

unsigned&nbsp;char&nbsp;f_I2cRead2(void)&nbsp;

{&nbsp;

unsigned&nbsp;char&nbsp;uc_ReturnData;&nbsp;

TWCR&nbsp;=&nbsp;(TWCR&0X8F)|(1&nbsp;&lt;&lt;&nbsp;TWINT)&nbsp;|&nbsp;(1&nbsp;&lt;&lt;&nbsp;TWEN);&nbsp;

while&nbsp;(!(TWCR&nbsp;&&nbsp;(1&nbsp;&lt;&lt;&nbsp;TWINT)));&nbsp;

if&nbsp;((TWSR&nbsp;&&nbsp;0xF8)&nbsp;!=&nbsp;0x58)&nbsp;&nbsp;

&nbsp;return&nbsp;0;&nbsp;

uc_ReturnData=TWDR;&nbsp;

return&nbsp;uc_ReturnData;&nbsp;

}&nbsp;

出0入0汤圆

发表于 2009-7-21 19:54:20 | 显示全部楼层
谢谢lz的程序!  对我帮助很大!

出0入0汤圆

发表于 2009-12-14 10:27:56 | 显示全部楼层
谢谢!

出0入0汤圆

发表于 2009-12-14 23:11:43 | 显示全部楼层
记号一下

出0入0汤圆

发表于 2009-12-16 13:06:07 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-19 17:29:35 | 显示全部楼层
LOVEMCU :
你好,你的LM75程序还没有贴完是吧!配置寄存器这里都看不到呢,我现在在用这个,能否贴完出来看看!

出0入0汤圆

发表于 2010-6-10 20:54:05 | 显示全部楼层
不明白这样做的话跟用软件做出来的IIC有什么区别??
倒觉得那个简单点....还不受端口限制.

出0入0汤圆

发表于 2010-6-15 10:56:38 | 显示全部楼层
好东西

出0入0汤圆

发表于 2011-10-14 17:08:10 | 显示全部楼层
有没有模拟i2c读lm75温度的程序呀!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-7 13:47

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表