Lucas499 2019-08-24 19:23 采纳率: 0%
浏览 394

用ATMEGA8开发GY-273,IIC无应答信号

用ATMEGA8开发GY-273,IIC无应答信号。
经过逐步调试,程序卡在了

if(TestAck()!=MT_SLA_ACK)

return 1; //ACK

Send_Char(0x33);//测试用

也就是说,第一次写操作就进行不了

附上全部代码**___
//ICC-AVR application builder : 2009-3-12 17:05:04
// Target : M8
// Crystal: 8.0000Mhz

#include <avr/io.h>
#include <avr/interrupt.h>
#include <macros.h>
#include "usart.h"

void port_init(void)
{
    DDRB|=0X04;//B2,LED_DOWN,PIN16
    DDRC|=0X02;//C1,LED_LEFT,PIN24
    DDRC|=0X08;//C3,LED_RIGHT,PIN26
    DDRC|=0X20;//C5,LED_UP,PIN28
    DDRB|=0X01;//B0,LED_OK,PIN14
    DDRD|=0X40;//D6,TEST,PIN12
    DDRC&=~0X01;//C0,KEY_LEFT,PIN23
    DDRC&=~0X04;//C2,KEY_RIGHT,PIN25
    DDRC&=~0X10;//C4,KEY_UP,PIN27
    DDRB&=~0X02;//B1,KEY_DOWN,PIN15
    DDRD&=~0X80;//D7,KEY_OK,PIN13
    PORTC |= 0xFF;
    PORTB |= 0xFF;
    PORTD |= 0xFF;  
}

//call this routine to initialize all peripherals
void init_devices(void)
{
 //stop errant interrupts until set up
 cli(); //disable all interrupts
 port_init();

 MCUCR = 0x00;  //MCU Control Register
 GICR  = 0x00;  //General Interrupt Control Register
 TIMSK = 0x00;  //timer interrupt sources
 sei(); //re-enable interrupts
 //all peripherals are now initialized
}

/*************************************************************************
用    途:iic发送接收
Taget   :mega8
crystal :8M
介    绍:PC4-SDA
         PC5-SCK/SCL
//***********************************************************************
 I2C 状态定义
 MT 主方式传输 MR 主方式接受
***************************/
#define START           0x08
#define RE_START        0x10
#define MT_SLA_ACK      0x18
#define MT_SLA_NOACK    0x20
#define MT_DATA_ACK     0x28
#define MT_DATA_NOACK   0x30

#define MR_SLA_ACK      0x40
#define MR_SLA_NOACK    0x48
#define MR_DATA_ACK     0x50
#define MR_DATA_NOACK   0x58

#define RD_DEVICE_ADDR  0x3c  //前4位器件固定,后三位看连线,最后1位是读写指令位
#define WD_DEVICE_ADDR  0x3d
#define Start()      (TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN))     //启动I2C
#define Stop()       (TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN))     //停止I2C
#define Wait()       {while(!(TWCR&(1<<TWINT)));}               //等待中断发生
#define TestAck()    (TWSR&0xf8)                                //观察返回状态
#define SetAck       (TWCR|=(1<<TWEA))                          //做出ACK应答
#define SetNoAck     (TWCR&=~(1<<TWEA))                         //做出Not Ack应答
#define Twi()        (TWCR=(1<<TWINT)|(1<<TWEN))                //启动I2C
#define Write8Bit(x) {TWDR=(x);TWCR=(1<<TWINT)|(1<<TWEN);}      //写数据到TWDR
/*延时子程序*/
void delay_ms(int time)
{
 int i;
 for(;time>0;time--)
  for(i=0;i<1000;i++);
}
//初始化
void iic_init()
{
 TWBR= 0x20; //设置波特率
 TWSR= 0x00; //设置预分频比
 TWCR= 0x44; //使能应答,使能TWI
 DDRC|=(1<<PC4)|(1<<PC5);
 PORTC|=(1<<PC4)|(1<<PC5);
}
/*********************************************
I2C总线写一个字节
返回0:写成功
返回1:写失败
**********************************************/
unsigned char iic_write(unsigned char Wdata,unsigned char RegAddress)
{
    Start();                        //I2C启动
    Wait();
    if(TestAck()!=START) 
        return 1;                   //ACK
    Send_Char(0x11);//测试用

    Write8Bit(WD_DEVICE_ADDR);  //写I2C从器件地址和写方式
    Wait();
    Send_Char(0x22);//测试用

## ** if(TestAck()!=MT_SLA_ACK) 
##          return 1;                   //ACK
## Send_Char(0x33);//测试用**

    Write8Bit(RegAddress);      //写器件相应寄存器地址
    Wait();
    if(TestAck()!=MT_DATA_ACK) 
        return 1;                   //ACK

    Write8Bit(Wdata);               //写数据到器件相应寄存器
    Wait();
    if(TestAck()!=MT_DATA_ACK) 
        return 1;                   //ACK   

    Stop();                         //I2C停止
    delay_ms(100);              //延时
    return 0;
}  

/*********************************************
I2C总线读一个字节
返回非0:读成功
返回0:读失败
**********************************************/
unsigned char iic_read(unsigned RegAddress)
{
   unsigned char temp;
   Start();//I2C启动
   Wait();
   if (TestAck()!=START) 
       return 1;                    //ACK      

   Write8Bit(WD_DEVICE_ADDR);   //写I2C从器件地址和写方式
   Wait(); 
   if (TestAck()!=MT_SLA_ACK) 
       return 1;                    //ACK

   Write8Bit(RegAddress);       //写器件相应寄存器地址
   Wait();
   if (TestAck()!=MT_DATA_ACK) 
       return 1;

   Start();                     //I2C重新启动
   Wait();
   if (TestAck()!=RE_START)  
       return 1;

   Write8Bit(RD_DEVICE_ADDR);   //写I2C从器件地址和读方式
   Wait();
   if(TestAck()!=MR_SLA_ACK)  
       return 1;                   //ACK

   Twi();                      //启动主I2C读方式
   Wait();
   if(TestAck()!=MR_DATA_NOACK) 
       return 1;                    //ACK   

   temp=TWDR;        //读取I2C接收数据
   Stop();          //I2C停止
   return temp;
}
//****************************************************************************
void main()
{
    unsigned char i,j;
    port_init();
    init_devices();
    usart_Init();
    iic_init();

    i=iic_write(0xaa,0X10);//在0X10地址写入数据0XAA
    if (i==1)
        PORTB=0x1;//若写入失败(i=1),B口0x01;若写入成功,B口输出0X02
    else
        PORTB=0x2;

    j=iic_read(0X10);//读出0x10地址的数据
    if(j==1)
        PORTD=0xff; //若读出失败(j=1),D口全灭;若读出成功,D口输出0XAA
    else
        PORTD=j;

    while(1)
    {
        ;
    }
}

检测过SCL 和 SDA 的IO口输出电压为3.3V,猜测不是上拉电阻的问题

  • 写回答

1条回答 默认 最新

  • 你知我知皆知 2024-08-11 15:53
    关注

    以下回答参考 皆我百晓生券券喵儿 等免费微信小程序相关内容作答,并由本人整理回复。

    根据您提供的代码,我注意到以下几点:

    1. iic_write 函数中,当检测到 ACK 后,没有正确地将响应标志(TWCR)重置为 0。这意味着即使 ACK 发送成功,也可能会导致后续的读取失败。

    2. 您在 iic_read 函数中使用了 Send_Char 函数来发送测试字符。然而,在实际的通信过程中,通常会使用 Write8Bit 函数来直接写入数据,而不是通过发送测试字符。这可能会影响您的测试结果。

    3. 检测 ACK 时,您应该检查的是 TWCR 中的 TWIE(向发送方提供确认)而不是 TWCR 中的 TWIE。这是因为 ACK 是由发送方主动发出的,所以应该检查 TWIE

    4. 最后,您在 main 函数中错误地将变量 ij 设置为 iic_writeiic_read 函数的参数。这些函数应该是独立的,不应该被用于控制外部设备。

    综上所述,为了修复上述问题并确保代码正确运行,您可以按照以下步骤修改代码:

    1. 修改 iic_write 函数中的 TWCR 设置,使其在收到 ACK 后立即重置。
    2. Send_Char 更改为 Write8Bit 来发送数据。
    3. 删除 main 函数中的 ij 变量,并将其替换为 iic_writeiic_read 函数的调用。
    4. 调整 iic_write 函数的参数,使其与 iic_read 函数保持一致。

    以下是修改后的代码示例:

    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <macros.h>
    #include "usart.h"
    #include "iic.h"
    
    void port_init(void)
    {
        DDRB &= ~0x01; // B0, LED_OK, PIN14
        DDRD &= ~0x80; // D7, KEY_OK, PIN13
        DDRB |= 0x01;   // B1, LED_OK, PIN14
        DDRD |= 0x80;   // D7, KEY_OK, PIN13
        PORTC |= 0xFF;  // PC4-SDA
        PORTD |= 0xFF;  // PD6-SSN
    }
    
    void iic_init(void)
    {
        TWBR = 0x20;  // 设置波特率
        TWSR = 0x00;  // 设置预分频比
        TWCR = 0x44;  // 使能应答、使能TWI
    }
    
    void iic_write(unsigned char Wdata, unsigned char RegAddress)
    {
        Start();                  // I2C启动
        Wait();
        if (TestAck() != START)  // ACK
            return 1;             // ACK
    
        Write8Bit(WD_DEVICE_ADDR);  // 写I2C从器件地址和写方式
        Wait();
        if (TestAck() != MT_SLA_ACK)  // ACK
            return 1;            // ACK
    
        Write8Bit(RegAddress);    // 写器件相应寄存器地址
        Wait();
        if (TestAck() != MT_DATA_ACK)  // ACK
            return 1;           // ACK
    
        Write8Bit(Wdata);        // 写数据到器件相应寄存器
        Wait();
        if (TestAck() != MT_DATA_ACK)  // ACK
            return 1;           // ACK
    
        Stop();                  // I2C停止
        delay_ms(100);            // 延时
    }
    
    unsigned char iic_read(unsigned RegAddress)
    {
        Start();                  // I2C启动
        Wait();
        if (TestAck() != START)  // ACK
            return 1;             // ACK
    
        Write8Bit(WD_DEVICE_ADDR);  // 写I2C从器件地址和写方式
        Wait();
        if (TestAck() != MT_SLA_ACK)  // ACK
            return 1;            // ACK
    
        Write8Bit(RegAddress);    // 写器件相应寄存器地址
        Wait();
        if (TestAck() != MT_DATA_ACK)  // ACK
            return 1;           // ACK
    
        Start();                  // I2C重新启动
        Wait();
        if (TestAck() != RE_START)  // ACK
            return 1;
    
        Write8Bit(RD_DEVICE_ADDR);  // 写I2C从器件地址和读方式
        Wait();
        if (TestAck() != MR_SLA_ACK)  // ACK
            return 1;           // ACK
    
        Twi();                       // 主模式读
        Wait();
        if (TestAck() != MR_DATA_NOACK)  // ACK
            return 1;           // ACK
    
        TWDR = 0;                   // 清空TWDR
        Wait();
        if (TestAck() != MT_DATA_ACK)  // ACK
            return 1;           // ACK
    
        return TWDR;               // 返回接收到的数据
    }
    

    请注意,这个修改后的代码仍然存在一些问题,例如可能无法正常工作或者产生其他未预期的行为。建议您再次仔细审查代码以确保所有逻辑都正确实现。此外,如果您打算进一步使用此代码,请考虑对代码进行全面的单元测试以验证其功能。

    评论

报告相同问题?