打印

attiny9模拟I2C协议程序工作不正常

[复制链接]
752|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
hstone11|  楼主 | 2017-2-24 20:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
烧写了attiny9程序,感觉总是不正常,好像根本没有跑起来,有高手指点一二吗?

#define F_CPU 4000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>


#define uchar unsigned char
#define uint unsigned int



        #define write_ADD 0x88
        #define read_ADD 0x89
       
        #define CONF_ADDR 0x01
        #define INTER_ADDR 0x02
        #define PROX_LT        0x03
        #define PROX_HT        0x04
        #define PROX_DATA 0x08
       
        #define CONF_PROXREG 0xA8//prox sensor is enable and the sleep time is 200ms(1010 1000)
        #define CONF_INTREG  0x00
       
       


#define I2C_SDA_OUT DDRB |= (1 << PORTB3)
#define I2C_SDA_IN  DDRB &= ~(1 << PORTB3)
#define I2C_SCL_OUT DDRB |= (1 << PORTB0)
#define I2C_SCL_IN  DDRB &= ~(1 << PORTB0)

#define SDA_WRT_H PORTB |= (1 << PORTB3)
#define SDA_WRT_L PORTB &= ~(1 << PORTB3)
#define SCL_WRT_H PORTB |= (1 << PORTB0)
#define SCL_WRT_L PORTB &= ~(1 << PORTB0)

#define ALERT_ON  PORTB |= (1 << PORTB2)
#define ALERT_OFF PORTB &= ~(1 << PORTB2)
//#define SDA_RED_H (PINB0 == 1)
#define SDA_READ        PINB3
void init_sys(void);
void I2c_init();                         //初始化

uchar I2c_respond();  //应答 SCL在高电平期间,SDA被从设备拉为低电平表示应答
void I2c_ACK(void);
void I2c_Start(void);
void I2c_Stop(void);
uchar I2c_write_byte(uchar dat);//写字节
void I2c_write(uchar addr,uchar dat);         //指定地址写
uchar I2c_read_byte(void);                        //读字节
uchar I2c_read(uchar addr);     //指定地址读
uchar g_bflag = 0;                //应答标志位
uchar g_ProxVal = 0;        //接近程度值
uchar g_proxH = 0xFF;        //接近度报警值

uint g_iTimebase = 0xFF;
void Conf_reg(void);
void SetProxVal(uchar prox_h, uchar prox_l);
void IsAlert(void);
//定时器初始化
void Timer_Init();
void Timer_changeMs(uchar ms100);//unit:100 ms
void SetSleepMode(uchar ms100);//unit:100ms
//定时器初始化
void Timer_Init()
{       
        TCCR0B = 0x05; //预分频1024
        g_iTimebase = F_CPU / 1024.0 * 0.1;//0.1s
        //Timer_changeMs(2);//0.2s中断
}
void Timer_changeMs(uchar ms100)
{
        //max is 16s
        if(ms100 > 160)
                ms100 = 160;
        uint uTimes = g_iTimebase * ms100;
        TCNT0L = 256 - (uTimes & 0xFF); //
        TCNT0H = 256 - ((uTimes >> 8) & 0xFF);
       
        TIMSK0 = 0x01; //使能T0中断
        //sei(); //开中断
}

ISR(TIM0_OVF_vect)
{
}
void SetSleepMode(uchar ms100)
{
         cli(); //disable all interrupts
         Timer_changeMs(ms100);
         sei(); //开中断
}
void init_sys(void)
{       
        //BBMB = 1;//break-before-make:pin input to output
        PORTCR = 0x01;
        I2C_SDA_OUT;
        I2C_SCL_OUT;       
}                  
//***************************************************************************
////开始信号
//在时钟线SCL为高电平时,数据线SDA由高向低跳变.
void I2c_Start()   
{     
        SDA_WRT_H;
        SCL_WRT_H;         
         
    _delay_us(5);
     
    //SDA = 0;
        SDA_WRT_L;  
    _delay_us(5);
       
        //SCL = 0;
        SCL_WRT_L;
        _delay_us(5);
}  
//***************************************************************************  
void I2c_Stop()   //停止 SCL在高电平期间,SDA一个上升沿则表示停止信号  
{  
     SDA_WRT_L;
     _delay_us(5);
                  
     SCL_WRT_H;
     _delay_us(5);
         
     SDA_WRT_H;  
}
//***************************************************************************  
uchar I2c_respond()  //应答 SCL在高电平期间,SDA被从设备拉为低电平表示应答  
{  
    uchar ACK = 0;
        SDA_WRT_H;
        SCL_WRT_H;       
       
        I2C_SDA_IN;
        _delay_us(5);
       
        ACK = ~SDA_READ;//?
        SCL_WRT_L;
        _delay_us(5);
        I2C_SDA_OUT;
        return ACK;   
}  
//***************************************************************************  
void I2c_init()//总线初始化 将总线都拉高一释放总线  发送启动信号前,要先初始化总线。即总有检测到总线空闲才开始发送启动信号  
{  
    //SDA = 1;  
        SDA_WRT_H;
    _delay_us(5);
         
    //SCL = 1;  
        SCL_WRT_H;
    _delay_us(5);
}  
//***************************************************************************  
uchar I2c_write_byte(uchar dat) //写一个字节  
{  
    uchar i;      
       
    for(i = 0; i < 8; i++)  
    {  
                if(((dat << i) & 0x80) > 0)
                {
                        SDA_WRT_H;
                }
                else
                {
                        SDA_WRT_L;
                }
                       
        //SCL = 1;
                SCL_WRT_L;
        _delay_us(5);
        //SCL = 0;
                SCL_WRT_L;
        _delay_us(5);
    }  
    //发送一个字节后,读取来自I2C芯片的响应.
    //即在发送完8个字节后,在第九个时钟内,SDA总线应为低电平,表示I2C芯片已经读取了数据.
    //否则为高电平.即发送败.  
        return I2c_respond();
}  
//***************************************************************************  
uchar I2c_read_byte(void)//读一个字节  
{  
    uchar i,k = 0;  
     
    //SDA = 1;
        SDA_WRT_H;   
        I2C_SDA_IN;
    for(i = 0; i < 8; i++)  
    {  
                SCL_WRT_H;
        //SCL = 1;//上升沿时,IIC设备将数据放在sda线上,并在高电平期间数据已经稳定,可以接收啦  
        _delay_us(5);
                     
        k = (k << 1) | SDA_READ;  
        //SCL = 0;//拉低SCL,使发送端可以把数据放在SDA上  
                SCL_WRT_L;
        _delay_us(5);
    }  
    return k;  
}  
//***************************************************************************  
void I2c_write(uchar addr, uchar dat)//任意地址写一个字节  
{  
    I2c_Start();//启动  
    I2c_write_byte(write_ADD);//发送从设备地址  
    I2c_respond();//等待从设备的响应  
    I2c_write_byte(addr);//发出芯片内地址  
    I2c_respond();//等待从设备的响应  
    I2c_write_byte(dat);//发送数据  
    I2c_respond();//等待从设备的响应  
    I2c_Stop();//停止  
}  
//***************************************************************************  
uchar I2c_read(uchar addr)//读取一个字节  
{  
    uchar dat;  
    I2c_Start();//启动  
    I2c_write_byte(write_ADD);//发送从设备地址 写操作  
    I2c_respond();//等待从设备的响应  
    I2c_write_byte(addr);//发送芯片内地址  
    I2c_respond();//等待从设备的响应  
    I2c_Start();//启动  
    I2c_write_byte(read_ADD);//发送从设备地址 读操作  
    I2c_respond();//等待从设备的响应  
    dat = I2c_read_byte();//获取数据  
    I2c_Stop();//停止  
    return dat;//返回数据  
}
void Conf_reg(void)
{
        uchar prox_l = 25;//FSR10%
        uchar prox_h = 50;//FSR20%
        uchar prox_max = 100;//FSR40%
        I2c_write(CONF_ADDR, CONF_PROXREG);//prox sensor enable and sleep mode 200ms
        I2c_write(INTER_ADDR, 0);
       
        prox_h = I2c_read(PROX_DATA);
       
        if(prox_h < prox_max && prox_h > prox_l)
        {
                prox_l = prox_h;               
        }
        g_ProxVal = prox_l << 1;
        I2c_write(PROX_LT, prox_l);
        I2c_write(PROX_HT, g_ProxVal);
}
int main(void)
{
        uchar prox_data = 0;
        //_delay_ms(1000);
        init_sys();
        I2c_init();
       
        Conf_reg();
    while(1)
    {
        //TODO:: Please write your application code
                prox_data = I2c_read(PROX_DATA);
                if(prox_data >= g_ProxVal)
                {
                        ALERT_ON;
                        //_delay_ms(5000);
                        SetSleepMode(50);//5s
                }
                //_delay_ms(205);
                ALERT_OFF;
                SetSleepMode(2);//0.2s               
    }
}

相关帖子

沙发
NE5532| | 2017-2-24 21:42 | 只看该作者
从机对地址帧应答了没有?

使用特权

评论回复
板凳
dirtwillfly| | 2017-2-25 10:35 | 只看该作者
建议用逻辑分析仪或者示波器观察一下时序,或者通讯成功点个led什么的,不要凭感觉

使用特权

评论回复
地板
hstone11|  楼主 | 2017-3-1 17:37 | 只看该作者
写了个最简单的程序下载后都没有反应,是不是下载时候设置不对吗?使用Atmel studio6.2编译并下载,器件选择attiny9(TPI),symbols中设置 F_CPU=4000000UL.烧录显示OK,上电后就是没有输出。
#define F_CPU 4000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
int main(void)
{
        DDRB = 0xFF;
       
        PORTB = 0;
        _delay_ms(1000);
    while(1)
    {
        //TODO:: Please write your application code
                PORTB = 0xFF;
                _delay_ms(5000);
                PORTB = 0;
                _delay_ms(5000);
    }
}

使用特权

评论回复
5
linqing171| | 2017-3-1 19:52 | 只看该作者
#define I2C_SDA_OUT DDRB |= (1 << PORTB3)
#define I2C_SDA_IN  DDRB &= ~(1 << PORTB3)
#define I2C_SCL_OUT DDRB |= (1 << PORTB0)
#define I2C_SCL_IN  DDRB &= ~(1 << PORTB0)

#define SDA_WRT_H I2C_SDA_IN
#define SDA_WRT_L I2C_SDA_OUT;PORTB &= ~(1 << PORTB3)
#define SCL_WRT_H PORTB |= (1 << PORTB0)
#define SCL_WRT_L PORTB &= ~(1 << PORTB0)

使用特权

评论回复
6
linqing171| | 2017-3-1 19:57 | 只看该作者
uchar I2c_write_byte(uchar dat) //写一个字节  
{  
    uchar i;   

这个函数进来要把SCL先拉低(其实无论从start还是从这个函数之后调用这个函数,SCL已经确保是低),然后就是for循环里你一直SCL发低,连个时钟脉冲都没有。

使用特权

评论回复
7
linqing171| | 2017-3-1 20:03 | 只看该作者
I2c_read_byte();后,你应该发送个ACK或者NACK。你给别人写的时候,别人给你应答; 别人给你输出后,你要用应答或者不应答来表示后面是否是stop,通知总线收发方向转换。

SCL和SDA无论发送 地址 数据 还是应答的时候,都必须是上拉电阻拉高或者一方拉低。 标准禁止强制输出1,但是你点对点通讯一切都在掌握之中的话,只要是注意一下也问题不大。
start后,stop前,每个函数退出前和进入后都首先保证SCL为低占住总线,防止被别人抢走。 如果点对点通讯的话总波形能这样就行了,对单个函数不强制要求。

bug太多。。。。

使用特权

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

本版积分规则

1

主题

3

帖子

0

粉丝