打印
[AVR单片机]

请教一下ATMEGA16引脚模拟SMBus读取MLX90614

[复制链接]
1582|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
复仇之矛|  楼主 | 2015-12-27 22:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
沙发
复仇之矛|  楼主 | 2015-12-27 22:58 | 只看该作者
二楼贴出我的实验程序
/********Done. Mon Dec 21 11:40:37 2015***********/
/*
MLX90614BAA
非接触温度传感器
额定电压+3V,最大耐压值+3.6V
*/
#include <iom16v.h>                        //包含型号头文件
#include <macros.h>                        //包含"位"操作头文件
#include <stdio.h>                        //标准输入输出头文件


#define uchar          unsigned char
#define uint           unsigned int
//#define RAM                 0x00    //单个MLX时,可以使用0x00进行访问
//#define EEPROM                 0x01
#define SEGLK PD2        //数码管段选锁存器控制端
#define BITLK PD3        //数码管位选锁存器控制端
//#define ack     0
//#define nack    1
#define SCL  0
#define SDA  1

uchar temp=333;

const SEGMENT[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d, 0x07,0x7f,0x6f,0x77,
                                   0x7c,0x39,0x5e,0x79,0x71};
/************************
函数名称:PORT_Init()
函数功能:初始化端口
函数说明:对于不使用的IO引脚,设置为上拉输入
**********************/
void PORT_Init(void)
{
  DDRA=0X00;   //A端口暂时不涉及
  PORTA=0XFF;

  DDRB=0XFF;   //B端口数码管
  PORTB=0XFF;

  DDRC=0X03;   //C端口设计为模拟SMBus
  PORTC=0XFF;

  DDRD=0X0C;   //D端口涉及位选、段选、蜂鸣器(蜂鸣器位设置为上拉输入,禁用蜂鸣器)
  PORTD=0XFF;
}
/**************************
函数名称:Delay_us(uchar N)
函数功能:延时微秒
**************************/
void Delay_us(uint N)
{
uint i;
N=N*5/4;
for(i=0;i<N;i++);
}
/*******************************************
函数名称: Delay_ms
功    能: 延时指定毫秒(8M晶振)
参    数: MS--延时的毫秒数
/********************************************/
void Delay_ms(uint MS)                  
{
uint i,j;
for( i=0;i<MS;i++)
for(j=0;j<1141;j++);        //1141是在8MHz晶振下,通过软件仿真反复实验得到的数值
}
/*******************************************
函数名称: One_smg_display
功    能: 指定的数码管显示指定的内容
参    数: data--显示的内容(0-15),number--指定的数码管(1-3)                    
/********************************************/
void One_smg_display(uchar data,uchar number)
{
PORTB=0X00;
PORTD|=BIT(SEGLK);                   //更新段选   
PORTB|=SEGMENT[data];           //输出段选  
PORTD&=~BIT(SEGLK);           //锁存段选

PORTB=0XFF;
PORTD|=BIT(BITLK);                   //更新位选
switch(number)
{
  case 1ORTB&=~0X84;break;
  case 2ORTB&=~0X82;break;
  case 3ORTB&=~0X81;break;
}
PORTD&=~BIT(BITLK);           //锁存位选
Delay_ms(1);            //900时间有些长,造成闪屏Dec 22 08:09:58 2015
}
/***********************************************
函数名称:smbus_start()
功能:smbus总线开始信号
******************************************/
void smbus_start(void)
{
PORTC|=BIT(SCL);
PORTC|=BIT(SDA);                                                   
Delay_us(5);                 
PORTC&=~BIT(SDA);         
Delay_us(6);
PORTC&=~BIT(SCL);
Delay_us(3);
}
/*****************************************
函数名称:smbus_stop()
函数功能:smbus总线停止
****************************************/
void smbus_stop(void)
{
PORTC&=~BIT(SCL);                 
PORTC&=~BIT(SDA);                  
Delay_us(5);
PORTC|=BIT(SCL);                 
Delay_us(6);
PORTC|=BIT(SDA);
}
/******************************
函数名称:read_slave_ack()
函数功能:接收一个回应位
*************************/
uchar read_slave_ack(void)
{
uchar ack_nack;

DDRC&=~BIT(SDA);
PORTC|=BIT(SCL);
Delay_us(4);
if(PIND&(1<<1))
ack_nack=1;            //1高电平表示非应答信号
else
ack_nack=0;         //0低电平表示应答信号
PORTC&=~BIT(SCL);
DDRC|=BIT(SDA);
Delay_us(4);

return ack_nack;
}
/*******************************
函数名称:smbus_read_bit()
函数功能:读取一位
****************************/
uchar smbus_read_bit(void)
{
uchar in_bit;

DDRC&=~BIT(SDA);  //在引脚设置完成和读取引脚电平值之间,要添加一个系统时钟周期
PORTC|=BIT(SCL);
Delay_us(4);
if(PIND&(1<<1))
in_bit=1;         
else
in_bit=0;        
PORTC&=~BIT(SCL);
DDRC|=BIT(SDA);
Delay_us(4);

return in_bit;
}
/****************************
函数名称:smbus_send_bit()
函数功能:发送一位
*****************************/
void smbus_send_bit(uchar out_bit)
{
uchar t=5;
if(out_bit==1)
PORTC|=BIT(SDA);
else
PORTC&=~BIT(SDA);
while(t--);    //约250ns  Dec 22 14:59:59 2015
PORTC|=BIT(SCL);
Delay_us(15);
PORTC&=~BIT(SCL);
Delay_us(5);
}
/*****************************************
函数名称:smbus_readbyte()
函数功能:读一个字节
参数:    读命令
返回值:  读的值
****************************************/
uint smbus_readbyte(uint ack_nack)
{
uint i=0;
uint data=0;

for(i=8;i>0;i--)           //接收8位数据
{
  if(smbus_read_bit()==1)  //判断SDA是否是高位
  {
   data<<=1;
   data=data|0x01;
  }
  else
  {
  data=data<<1;
  data&=~0xfe;
  }
}
smbus_send_bit(ack_nack);

return data;
}
/*****************************************
函数名称:smbus_writebyte()
函数功能:写一个字节
参数:    data
返回值:返回1成功,返回0失败
****************************************/
uchar smbus_writebyte(uint data)
{
uchar i=0;
uchar bit;
uchar ack_nack;

for(i=8;i>0;i--)
{
  if((data&0x80)==0x80)
  bit=1;
  else
  bit=0;
  smbus_send_bit(bit);
  data<<=1;
}
ack_nack=read_slave_ack();    //Dec 22 15:34:30 2015

return ack_nack;
}
/*************************
函数名称:pec_cal()
函数功能:计算PEC值,使用定义好的CRC校验和计算方式
参数:被计算数组,数组长度
返回值:计算结果
**************************/
uint pec_cal(uchar pec[])
{
uchar crc[6];                 //存储初始校验值
uchar bitposition=47;
uchar shift;
uchar i;
uchar j;
uchar temp;
do
  {
   crc[5]=0;                   //载入CRC数值0x000000000107
   crc[4]=0;
   crc[3]=0;
   crc[2]=0;
   crc[1]=0x01;
   crc[0]=0x07;                       
   bitposition=47;             //设置bitposition的最大值为47
   shift=0;                    //在传送的字节中找出第一个1
   i=5;                        //设置最高标志位(包裹字节标志)
   j=0;                        //字节位标志,从最低位开始
   while((pec[i]&(0x80>>j))==0&&(i>0))
   {
    bitposition--;
        if(j<7)
         j++;
        else
        {
         j=0x00;
         i--;
        }
   }                           //while语句结束,并找出bitposition中为1的最高位位置
   shift=bitposition-8;        //得到CRC数值将要左移/右移的数值shift
   while(shift)                //对CRC数据左移shift位
   {
    for(i=5;i<0xff;i--)
        {
         if((crc[i-1]&0x80)&&(i>0)) //核对字节的最高位的下一位是否为1
          temp=1;                   //是,当前字节+1
         else
          temp=0;                   //否,当前字节+0
         crc[i]<<=1;
         crc[i]+=temp;
        }
        shift--;
   }
   for(i=0;i<=5;i++)            //pec和crc之间进行异或计算
   {
    pec[i]^=crc[i];     
   }
  }
  while(bitposition>8);

  return pec[0];                         //返回计算所得的crc数值
}

/*****************************************
函数名称:smbus_readram()
函数功能:读RAM中的值
参数:地址addr,命令cmd
返回值:数值
****************************************/
uint smbus_readram(uchar addr,uchar cmd)
{
unsigned long int data;                                 //存储处理后的温度值
uchar datal,datah;                                               //存储读数据的低8位和高8位
uchar pec;                                                       //packet errer count
uchar buf[6];                                                   //计算校验和的临时缓存数组
uchar ack_nack;                                                  //接收回应值
uchar pecreg;
uchar slave_addr;
   slave_addr=addr<<1;
do
{
Begin:
      smbus_start();                                                 //发送开始信号
            smbus_writebyte(slave_addr);          //写地址,接收回应值
          //Dec 23 08:49:32 2015
          //ack  表示应答   SDA低电平
          //nack 表示非应答 SDA高电平
          //如果使用if(read_slave_ack()==0),0==0使得if()成立,表明接收到应答
          //如果使用if(read_slave_ack()==1),1==1使得if()成立,表明接收到非应答
            if(read_slave_ack())                                               //从设备接收失败
            {
             smbus_stop();               
             goto Begin;                   //写命令,接收回应值
            }
            smbus_writebyte(cmd);
            if(read_slave_ack())                                   //从设备接收成功,否则重新开始循环
             {
              smbus_stop();
           goto Begin;
             }
          smbus_start();                                             //重新发送开始信号
      smbus_writebyte(slave_addr|0x01);           //写地址,接收回应值
             if(read_slave_ack())
             {
       smbus_stop();
           goto Begin;
             }
          datah=smbus_readbyte(0);                         //读高8位值
          datal=smbus_readbyte(0);                        //读低8 位值
          pec=smbus_readbyte(1);                         //接收校验值
          smbus_stop();
          buf[5]=slave_addr;
          buf[4]=cmd;
          buf[3]=slave_addr|0x01;
          buf[2]=datal;
          buf[1]=datah;
          buf[0]=0;
          pecreg=pec_cal(buf);                                 //计算校验值
   }while(pecreg!=pec); //if received and calculated CRC are equal go out from da-while
          data=(datah<<8+datal)*0.02-273.15;

          return data;
}
/*******************************************
函数名称: main
功    能: 独立按键驱动主函数
参    数: 无
返回值  : 无
/********************************************/
void main(void)
{
uchar i=0;
//uchar slave_addr;
PORT_Init();                             //端口初始化
/*PORTC&=~BIT(SCL);
Delay_ms(3);
PORTC|=BIT(SCL);*/
while(1)
{
  i++;
  if(i>2)
   {
   //slave_addr=smbus_readram(0x00,0x2e);
   temp=smbus_readram(0x5a,0x07);
   Delay_ms(15);
   i=0;
   }
   temp=temp/1;
   One_smg_display(temp/100,1);
   One_smg_display(temp%100/10,2);
   One_smg_display(temp%10,3);
}
}

使用特权

评论回复
板凳
复仇之矛|  楼主 | 2016-3-2 10:18 | 只看该作者
.
程序已经调试完成,问题已经解决,结贴喽

使用特权

评论回复
地板
红海ISRAEL| | 2019-2-4 17:36 | 只看该作者
可以介绍下解决过程么?也遇到类似问题,不能收到返回的ACK

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

23

主题

185

帖子

3

粉丝