[经验] 有关msp430fg479和bq27410(单片机主发送)

Jacktang   2015-11-30 08:54 楼主
先用msp430fg479内部的IIC模块来做,后用I/O口模拟来做。

先产生一个START信号;
UCBxTXIFG位自动置位;
发送从地址(从地址不用写入UCBxTXBUF寄存器中;如果在发送从地址的时候仲裁不会丢失,则写入UCBxTXBUF寄存器中的数据会被发送);
一旦从设备告知已收到地址acknowledge后,UCTXSTT位自动清除,紧接着发送数据。

写从地址时,只需写好7位地址就行了,读写位需要单独设置,这是我通过调试和观察波形得到结果,因为我从地址设置成0xff输出的值却是0xfe.
试验证明是正确的,从地址写成0xff和0x7f,输出的波形都一样的,所以UCB0I2CSA寄存器中的从地址只取8位数据中的低7位。
那么读写位在哪里控制呢?
很简单,读写位用UCTR来控制,
0 Receiver
1 Transmitter
0表示接受, R/W = 1;
1表示发送,R/W = 0;
可以和起始信号一起设置UCB0CTL1 |= UCTR + UCTXSTT;
When R/W = 0, the master transmits data to a slave. When R/W = 1,the master receives data from a slave

接受到地址后,可以收到ACK信号,但是下一个数据即寄存器地址却不能收到ACK,收到NACK高电平,也就是说IIC通信承认地址了,但是数据还是不承认。收到NACK信号后,SCK自动拉高,SDA自动拉低,等待SDA的一个上升沿信号来结束数据传输。(haha,上面这种现象是我写程序时只在发送缓存器中写了一个要发送的数据,发送完之后什么也没有做,所以最后有SCK拉高,SDA拉低,其实这就是ACK信号,大脑不清醒的话就不认识了,这个信号不是很清楚,所以发数据时要多发几个,以便把整个过程看都看都很清楚。)
终于成功了,数据可以全部接受,但是在这里还是要说一点,从波形上看,在发送完每个数据之后,要等待从设备发送ACK信号,这个时间要稍微长一点,所以在波形图上看到SCK自动拉低等待ACK信号的接受,此时,如果SCK的下一脉冲SDA是低电平,则接受到的是ACK信号,如果是高电平,则接受到的是NACK信号。

//******************************************************************************
//   MSP430F(G)47x Demo - USCI_B0 I2C Master TX multiple bytes to MSP430 Slave
//
//   Description: This demo connects two MSP430's via the I2C bus. The master
//   transmits to the slave. This is the master code. It continuously
//   transmits an array of data and demonstrates how to implement an I2C
//   master transmitter sending multiple bytes using the USCI_B0 TX interrupt.
//   ACLK = 32kHz, MCLK = SMCLK = TACLK = BRCLK = 1MHz
//

#include  

unsigned char *PTxData;                     // Pointer to TX data
unsigned char TXByteCtr;
const unsigned char TxData[] =              // Table of data to transmit
{
  0x00,
  0x0c,
  0x00,
  0x44,
  0x55
};

void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P3SEL |= BIT1+BIT2;                       // Assign I2C pins to USCI_B0
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
  UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     // I2C Master, synchronous mode
  UCB0CTL1 = UCSSEL_2 + UCSWRST;            // Use SMCLK, keep SW reset
  UCB0BR0 = 10;                             // fSCL = SMCLK/10 = 100kHz
  UCB0BR1 = 0;
  UCB0I2CSA = 0x55;                         // Slave Address is 055h/bq27410
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  IE2 |= UCB0TXIE;                          // Enable TX interrupt

  while (1)
  {
    PTxData = (unsigned char *)TxData;      // TX array start address
    TXByteCtr = sizeof TxData;              // Load TX byte counter
    UCB0CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
    __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
                                            // Remain in LPM0 until all data
                                            // is TX'd
    while (UCB0CTL1 & UCTXSTP);             // Loop until STP is TX'd
  }
}

//------------------------------------------------------------------------------
// The USCIAB0TX_ISR is structured such that it can be used to transmit any
// number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData
// points to the next byte to transmit.
//------------------------------------------------------------------------------
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
  if (TXByteCtr)                            // Check TX byte counter
  {
    UCB0TXBUF = *PTxData++;                 // Load TX buffer
    TXByteCtr--;                            // Decrement TX byte counter
  }
  else
  {
    UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
    IFG2 &= ~UCB0TXIFG;                     // Clear USCI_B0 TX int flag
    __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
  }
}

下面是用I/O来模拟实现IIC通信,最重要的是时序正确,但是这次时序没有问题,却出现了问题,这个问题表现出了用I/O来模拟IIC的不稳定性。

按照如下时序来做
360截图20151130085125898.jpg 360截图20151130085139735.jpg

波形:绿线是时钟,黄线是数据。从波形上看,在第9位上的数据是ACK信号(低电平),但是图上看到,产生ACK信号的前后都会出现一个干扰的波形,我们知道时钟线SCL在高电平的时候,数据SDA是必须保证稳定,一旦这个干扰信号进入SCL高电平区域内,就会被认为是NACK信号,所以有时能接受到数据,有时接受不到数据。

另外一个很不好的地方是接受ACK的时钟时间和前面传输数据每一个字节的时钟时间都是一样的,都是固定的,而实际上,主设备发送完数据后在等待从设备返回ACK响应信号的时候时间会变化的,时间也可能增加,从msp430fg479内部的IIC模块来做所测试的波形我们就能很清楚的看到。在没有接受到ACK或NACK信号之前SCL自动被拉低,直到SCL的第9个脉冲接受到一个信号。
360截图20151130085225342.jpg

上图红线划的是理论波形,而实际波形是这样的。见下图(待上传图片)

附件:

用I/O来模拟IIC的代码

main.c//能收到两个ACK
360截图20151130085452953.jpg
#include
#include "I2C.h"
//unsigned char *PTxData;                     // Pointer to TX data
//unsigned char TXByteCtr;
/*const unsigned char TxData[] =              // Table of data to transmit
{
  0x00,
  0x0c,
  0x00
};*/
void main()
{
// PTxData = (unsigned char *)TxData;      // TX array start address
   // TXByteCtr = sizeof TxData;              // Load TX byte counter
   // unsigned char bytectr;
    //data = *writeData;
    I2C_Start();
    I2C_Send_Byte(0xAA);
    I2C_Read_Ack();

    I2C_Send_Byte(0x00);   
    I2C_Read_Ack();
    I2C_Send_Byte(0x0C);   
    I2C_Read_Ack();
        I2C_Send_Byte(0x00);   
    I2C_Read_Ack();
    /*for(bytectr=1;bytectr     {   PTxData++;
        I2C_Send_Byte(*PTxData);
        I2C_Read_Ack();
    } */   
    I2C_Stop();
}



I2C.h

#ifndef __I2C
#define __I2C
#include "main.h"
//#include "I2C.c"

#define   SDA_OUT   (P3DIR |= BIT1)
#define   SDA_IN    (P3DIR &= ~BIT1)
#define   SDA_H     (P3OUT |= BIT1)
#define   SDA_L     (P3OUT &= ~BIT1)
#define   SCL_H     (P3OUT |= BIT2)
#define   SCL_L     (P3OUT &= ~BIT2)
#define   SDA       ((P3IN & BIT1)==BIT1)

#define   SCL_OUT     (P3DIR |= BIT2)
#define   SCL_IN     (P3DIR &= ~BIT2)

void I2C_Delay(void);
void I2C_Start(void);
void I2C_Stop(void);
void send_Zero(void);
void send_One(void);
void I2C_Send_Byte(uchar ctrl_Buf) ;
uchar I2C_Read_Ack(void);   //返回为:=1有ACK,=0无ACK
void I2C_Nack(void);
void I2C_Ack(void);
uchar I2C_Read_Byte(void);   //返回为:=1有ACK,=0无ACK
/*void Write_I2C_Device(uchar slave_ad,uchar rd,uchar config_data)
uchar Read_I2C_Device(uchar slave_ad,uchar rd)
void Write_I2C_Device(uchar slave_ad,uchar rd,uchar config_data)
uchar Read_I2C_Device(uchar slave_ad,uchar rd)
uchar Read_I2C_Device_2Bytes(uchar slave_ad,uchar rdl,uchar rdh)*/

#endif

I2C.c

#include "I2C.h"
//#include "Timer.h"
//#include "main.h"

//#define I2C
uchar DataReadBack1,DataReadBack2;
uchar K=0,n=0;
void I2C_Delay(void)
{
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
   // _NOP();
   // _NOP();
   // _NOP();
}
/*
void I2C_Delay(void)
{
  TimeTick = 0;
  TBCCTL0 |= CCIE;  
  while(TimeTick<5);
  TBCCTL0 &= ~CCIE;
}*/

/* 开始 I2C 总线传送 */
void I2C_Start(void)
{     

    SDA_H; //SDA = 1
    SDA_OUT;   
    //I2C_Delay();
    SCL_H; //SCL = 1
    SCL_OUT;
    I2C_Delay();
    _NOP();
    SDA_L; //SDA = 0
    I2C_Delay();
    I2C_Delay();
    SCL_L; //SCL = 0  

}


/* 结束 I2C 总线传送 */
void I2C_Stop(void)
{  
    SDA_L;
    SDA_OUT;      
    //I2C_Delay();
    SCL_H;
    I2C_Delay();
    I2C_Delay();
    SDA_H;
   // I2C_Delay();
}


/* 写 0 */
void send_Zero(void)
{     
   SCL_L;
   _NOP();
   _NOP();
   I2C_Delay();
   SDA_L;
   I2C_Delay();
   SCL_H;
   I2C_Delay();
   _NOP();
}

/* 写 1 */
void send_One(void)
{  
   SCL_L;
   _NOP();
   _NOP();
   I2C_Delay();
   SDA_H;
   I2C_Delay();
   SCL_H;
   I2C_Delay();
   _NOP();
}

/* I2C 总线发送一个字节 */
void I2C_Send_Byte(uchar ctrl_Buf)
{  
    SDA_OUT;  
    I2C_Delay();
    I2C_Delay();

    uchar i,tmp = 0x80;
    for(i=0;i<8;i++)
    {
        if((ctrl_Buf & tmp)== tmp)
        {
            send_One();      // 发送1
        }
        else
        {
            send_Zero(); // 发送0
        }
        tmp >>= 1;  // tmp右移一位
    }
}


uchar I2C_Read_Ack(void)   //返回为:=1有ACK,=0无ACK
{  
    uchar i;

    SCL_L;     
    _NOP();
    _NOP();
    I2C_Delay();     
    SDA_IN;
    I2C_Delay();
    SCL_H;
    I2C_Delay();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();

    if(SDA)
    {
       i=0;
       n++;
    }      
    else
    {
       i=1;
       K++;
    }      
    SCL_L;
    SDA_L;     
    SDA_OUT;      
    I2C_Delay();        
    return i;
}

/*  
uchar I2C_Read_Ack(void)   //返回为:=1有ACK,=0无ACK
{  
    uchar i;   
    if(P3OUT&BIT1)
    {
      SDA_IN;  
      _NOP();
      _NOP();
      _NOP();
      _NOP();
      SCL_L;
    }
    else
    {      
        SCL_L;
        _NOP();
        _NOP();
        _NOP();
        _NOP();
        SDA_IN;
    }  
    I2C_Delay();      
    I2C_Delay();
    SCL_H;
    I2C_Delay();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    _NOP();   
    _NOP();

    if(SDA)
    {
       i=0;
    }      
    else
    {
       i=1;
    }      
    SCL_L;
    SDA_L;     
    SDA_OUT;      
    I2C_Delay();        
    return i;
}  */


void I2C_Nack(void)
{        
    SDA_H;
    SDA_OUT;
    SCL_H;
    I2C_Delay();
    I2C_Delay();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    SCL_L;
    I2C_Delay();      
}

void I2C_Ack(void)
{      
    SDA_L;
    SDA_OUT;
    //I2C_Delay();
    SCL_H;
    I2C_Delay();
    I2C_Delay();
    _NOP();
    _NOP();
    _NOP();
    _NOP();
    SCL_L;
    I2C_Delay();

}

uchar I2C_Read_Byte(void)   //返回为:=1有ACK,=0无ACK
{  
    uchar i;
    uchar ucRDData = 0;//返回值
    uchar ucDataBit = 0;//每一个clk 接受到的数据   
    SDA_IN;
    I2C_Delay();      
    for(i = 0;i < 8;i++)
    {
        SCL_L;
        I2C_Delay();
        SCL_H;
        I2C_Delay();
        _NOP();
        _NOP();
        _NOP();
        _NOP();
        _NOP();
        _NOP();
        _NOP();
        _NOP();

        ucDataBit = SDA;
        ucRDData = ((ucRDData << 1) | ucDataBit);//将数据依次存入ucRDData

        SCL_L;
        I2C_Delay();
    }
    return(ucRDData);
}


/* 向I2C接口器件寄存器写数据
uchar I2C_ByteWrite(uchar devAddr, uchar regAddr, uchar data)
{
    if(!I2C_Start()) return 0;
       I2C_Send_Byte(devAddr);    //设置设备地址+W
    if(!I2C_ReadAck())  
          {   
            I2C_Stop();
            return 0;
          }
    I2C_Send_Byte(regAddr);
    I2C_ReadAck();
    I2C_Send_Byte(data);
    I2C_ReadAck();
    I2C_Stop();
    return 1;
}*/
#ifdef I2C
void Write_I2C_Device(uchar slave_ad,uchar rd,uchar config_data)
{
    UCB0I2CSA = slave_ad;
    TxBuf[0] = rd;
    TxBuf[1] = config_data;
    UCB0CTL1 |= UCTR + UCTXSTT;     
}

uchar Read_I2C_Device(uchar slave_ad,uchar rd)
{   
    UCB0I2CSA = slave_ad;
    TxBuf[0] = rd;   
    UCB0CTL1 &= ~UCTR;   
    UCB0CTL1 |= UCTXSTT;

}
#else
void Write_I2C_Device(uchar slave_ad,uchar rd,uchar config_data)
{
    slave_ad &= 0xFE;
    I2C_Start();
    I2C_Send_Byte(slave_ad);
    I2C_Read_Ack();

    I2C_Send_Byte(rd);
    I2C_Read_Ack();

    I2C_Send_Byte(config_data);
    I2C_Read_Ack();

    I2C_Stop();
}

uchar Read_I2C_Device(uchar slave_ad,uchar rd)
{   

    /**/slave_ad &= 0xFE;
    I2C_Start();
    I2C_Send_Byte(slave_ad);
    I2C_Read_Ack();


    I2C_Send_Byte(rd);   
    I2C_Read_Ack();         


    slave_ad |= 0x01;
    I2C_Start();
    I2C_Send_Byte(slave_ad);      
    I2C_Read_Ack();

    DataReadBack1 = I2C_Read_Byte();
    I2C_Nack();   
    I2C_Stop();
    return 0;
}

uchar Read_I2C_Device_2Bytes(uchar slave_ad,uchar rdl,uchar rdh)
{   

    slave_ad &= 0xFE;
    I2C_Start();
    I2C_Send_Byte(slave_ad);
    I2C_Read_Ack();

    I2C_Send_Byte(rdl);
    I2C_Read_Ack();

    I2C_Send_Byte(rdh);
    I2C_Read_Ack();


    slave_ad |= 0x01;
    I2C_Start();
    I2C_Send_Byte(slave_ad);
    I2C_Read_Ack();


    DataReadBack1 = I2C_Read_Byte();
    I2C_Ack();
    DataReadBack2 = I2C_Read_Byte();
    I2C_Nack();
    I2C_Stop();/**/
    return 0;
}

#endif

/*
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
      if(flag==0)
      {

        IFG2 &= ~UCB0TXIFG;
        flag++;
      }

      else if(flag==1)
      {

        UCB0TXBUF = 0x05;  
        flag++;
      }
      else if(flag==2)
      {

        UCB0TXBUF = 0x06;
        flag++;
      }

      else
      {
          UCB0CTL1 |= UCTXSTP;
          flag = 0;
      }
}
*/

回复评论 (1)

好长,有附件更好了
点赞  2015-11-30 10:14
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复