s5p6818裸机-i2c

前言

写文章的目的是想通过记录自己的学习过程,以便以后使用到相关的知识点可以回顾和参考。

I2C简介

I2C 是很常见的一种总线协议,I2C 是 NXP 公司设计的,I2C 使用两条线在主控制器和从
机之间进行数据通信。一条是 SCL(串行时钟线),另外一条是 SDA(串行数据线),这两条数据
线需要接上拉电阻,总线空闲的时候 SCL 和 SDA 处于高电平。I2C 总线标准模式下速度可以
达到 100Kb/S,快速模式下可以达到 400Kb/S。
I2C 是支持多从机的,也就是一个 I2C 控制器下可以挂多个 I2C 从设备,这些不同的 I2C
从设备有不同的器件地址,这样 I2C 主控制器就可以通过 I2C 设备的器件地址访问指定的 I2C
设备了,一个 I2C 总线连接多个 I2C 设备如图所示:
在这里插入图片描述

实现流程

实现i2c传输功能的步骤:
1、初始化i2c,包括通过I2CCLKENB启动i2c的时钟源PCLK,通过I2CCON设置时钟分频得到i2c_clk,通过I2CSTAT开启i2c功能。
2、建立i2c传输的数据结构
3、编写产生读/写开始信号的函数,产生读/写停止信号的函数,检查ack应答信号的函数
4、编写主机模式下的读/写函数

具体实现

1、初始化i2c
其中i2c_clk的时钟源——PCLK的开启需要到手册的时钟体系板块那里找到相应的寄存器,而实现通过分频得到i2c_clk就到手册的i2c板块那里找相应的寄存器,一般i2c_clk的频率是100KHz或者400KHz。
在这里插入图片描述

#define I2C0_Clk    (*(volatile unsigned int*)0xC00AE000)
#define I2C1_Clk    (*(volatile unsigned int*)0xC00AF000)
#define I2C2_Clk    (*(volatile unsigned int*)0xC00B0000)
/* 初始化i2c */
void i2c_init(I2C_Type *Base)
{
    Base->I2CSTAT &= ~(1<<4);   /* 关闭i2c */

    I2C0_Clk |=(1<<3); I2C1_Clk |=(1<<3); I2C2_Clk |=(1<<3);   /* 使能i2c0,1,2 的时钟源PCLK */
    Base->I2CCON |= (1<<6); /* PCLK/256 = 200/256 = 781KHz */
    Base->I2CCON &= (0xF<<0);
    Base->I2CCON |= (7<<0);   /* Tx clock = 781/7 = 111KHz */

    Base->I2CCON |= (1<<7); /* 1:发送允许ACK信号 ,0:发出NO ACK信号*/

    Base->I2CSTAT |= (1<<4);   /* 使能i2c */

}

2、建立i2c传输的数据结构

/* 相关宏定义 */
#define I2C_STATUS_OK				(0) /* 正常 */
#define I2C_STATUS_BUSY				(1) /* 在忙 */
#define I2C_STATUS_IDLE				(2) /* 空闲 */
#define I2C_STATUS_NOACK			(3) /* 发送NOACK信号 */
#define I2C_STATUS_ARBITRATIONLOST	(4) /* 仲裁丢失错误 */
#define I2C_STATUS_TIMEOUT			(5) /* 超时 */
#define I2C_STATUS_ADDRNAK			(6) /* 没收到ACK信号 */

enum i2c_direction
{
    i2c_write = 0,      /* 写数据 */
    i2c_read  = 1,      /* 读数据 */
};

/* 主机传输结构体 */
struct i2c_transfer
{
    unsigned char slaveAddress;      	/* 7位从机地址 			*/
    unsigned int subaddress;       		/* 寄存器地址			*/
    volatile char *data;    	        /* 数据缓冲区 			*/
    volatile unsigned int dataSize;  	/* 数据缓冲区长度 			*/
};

3、编写产生读/写开始信号的函数,产生读/写停止信号的函数,检查ack应答信号的函数,参考数据手册需要使用的寄存器有I2CSTAT,I2CDS,I2CCON

/* 发送read的开始信号 */
unsigned char i2c_master_start_r(I2C_Type *Base, unsigned char address, enum i2c_direction direction)
{
    //if(Base->I2CSTAT & (1<<5))  /* 检查i2c是否在忙 */
    //   return I2C_STATUS_BUSY;

    Base->I2CDS = (((unsigned char)address<<1) | ((direction == i2c_read)? 1:0));    /* 发送从设备地址及读写位 */
    Base->I2CSTAT = 0xB0;    /* 根据参考手册 read 0xB0(M/T Start),即产生一个start信号 */
    return 0;
}

/* 发送write的开始信号 */
unsigned char i2c_master_start_w(I2C_Type *Base, unsigned char address, enum i2c_direction direction)
{
    if(Base->I2CSTAT & (1<<5))  /* 检查i2c是否在忙 */
        return I2C_STATUS_BUSY;

    Base->I2CDS = (((unsigned char)address<<1) | ((direction == i2c_read)? 1:0));    /* 发送从设备地址及读写位 */
    Base->I2CSTAT = 0xF0;    /* 根据参考手册 write 0xF0(M/T Start),即产生一个start信号 */
    return 0;
}

/* 错误检查 */
unsigned char i2c_check_and_clear_error(I2C_Type *Base)
{
    //unsigned short timeout = 0xffff;
    /* 检查仲裁丢失错误 */
    if((Base->I2CSTAT & (1<<3)))
    {
        Base->I2CCON &= ~(1<<4);    /* 清标志位 */
        Base->I2CSTAT &= ~(1<<4);   /* 关闭i2c */
        Base->I2CSTAT |= (1<<4);    /* 重新打开i2c */
        return I2C_STATUS_ARBITRATIONLOST;
    }
    /* 检查是否接收到ack信号 */
    else if(Base->I2CSTAT & (1<<0))
    {
        return I2C_STATUS_NOACK;
    }
 
    return I2C_STATUS_OK;
}

/* 发送read停止信号 */
unsigned char i2c_master_stop_r(I2C_Type *Base)
{
    unsigned short timeout = 0xffff;

    Base->I2CSTAT = 0x90;    /* 根据参考手册 write 0xD0(M/T Start),即产生一个stop信号 */

    /* 等待忙完 */
    while(Base->I2CSTAT & (1<<5))
    {
        timeout--;
        if(timeout == 0)
            return I2C_STATUS_TIMEOUT;
    }
    return I2C_STATUS_OK;
}

/* 发送write停止信号 */
unsigned char i2c_master_stop_w(I2C_Type *Base)
{
    unsigned short timeout = 0xffff;

    Base->I2CSTAT = 0xD0;    /* 根据参考手册 write 0xD0(M/T Start),即产生一个stop信号 */

    /* 等待忙完 */
    while(Base->I2CSTAT & (1<<5))
    {
        timeout--;
        if(timeout == 0)
            return I2C_STATUS_TIMEOUT;
    }
    return I2C_STATUS_OK;
}

4、编写主机模式下的读/写函数
在参考手册中的截图:主机模式下的发送流程
在这里插入图片描述

/* 主机模式下的写操作 */
unsigned char i2c_master_write(I2C_Type *Base, struct i2c_transfer *vfer)
{
    //unsigned char error = 0;

    //Base->I2CCON &= ~(1<<4);                                  /* 清标志位 */
    i2c_master_start_w(Base, vfer->slaveAddress, i2c_write);    /* 发送start信号 */
    Base->I2CCON &= ~(1<<4);                                    /* 清标志位 */
    if(i2c_check_and_clear_error(Base))                         /* 检查ACK */
        return i2c_check_and_clear_error(Base);

    Base->I2CDS = vfer->subaddress;                             /* 要写入的寄存器地址 */
    Base->I2CCON &= ~(1<<4);                                    /* 清标志位 */
    if(i2c_check_and_clear_error(Base))                         /* 检查ACK */
        return i2c_check_and_clear_error(Base);

    while(vfer->dataSize--)
    {
        Base->I2CDS = *(vfer->data)++;
        Base->I2CCON &= ~(1<<4);                                /* 清标志位 */

        if(i2c_check_and_clear_error(Base))                     /* 检查ACK */
        {
            i2c_master_stop_w(Base);                            /* 出错时发送write停止信号 */
            Base->I2CCON &= ~(1<<4);                            /* 清标志位 */
            return i2c_check_and_clear_error(Base);
        }
    }
    i2c_master_stop_w(Base);                                    /* 发送write停止信号 */
    Base->I2CCON &= ~(1<<4);                                    /* 清标志位 */
    return I2C_STATUS_OK;
}

在参考手册中的截图:主机模式下的接收流程
在这里插入图片描述

/* 主机模式下的读操作 */
unsigned char i2c_master_read(I2C_Type *Base, struct i2c_transfer *vfer)
{
    volatile char unusedata = 0;

    unusedata++;

    i2c_master_start_w(Base, vfer->slaveAddress, i2c_write);    /* 发送write的start信号 */
    Base->I2CCON &= ~(1<<4);                                    /* 清标志位 */
    if(i2c_check_and_clear_error(Base))                         /* 检查ACK */
        return i2c_check_and_clear_error(Base);

    Base->I2CDS = vfer->subaddress;                             /* 要读取的寄存器地址 */
    Base->I2CCON &= ~(1<<4);                                    /* 清标志位 */
    if(i2c_check_and_clear_error(Base))                         /* 检查ACK */
        return i2c_check_and_clear_error(Base);

    i2c_master_start_r(Base, vfer->slaveAddress, i2c_read);     /* 发送read的start信号 */
    Base->I2CCON &= ~(1<<4);                                    /* 清标志位 */
    if(i2c_check_and_clear_error(Base))                         /* 检查ACK */
        return i2c_check_and_clear_error(Base);

    if(vfer->dataSize == 1)
        Base->I2CCON &= ~(1<<7);                                /* 发送NO ACK信号 */

    unusedata = Base->I2CDS;                                    /* 假读,第一个数据没用,不要 */

    Base->I2CCON &= ~(1<<4);                                    /* 清标志位 */
    if(i2c_check_and_clear_error(Base))                         /* 检查ACK */
        return i2c_check_and_clear_error(Base);
    
    while(vfer->dataSize--)
    {
        if(vfer->dataSize == 1)
        {
            Base->I2CCON &= ~(1<<7);                /* 在接收倒数第二个数据时发送NO ACK信号 */
        }

        *(vfer->data)++ = Base->I2CDS;                          /* 接收数据 */

        if(i2c_check_and_clear_error(Base))                     /* 检查ACK */
        {
            i2c_master_stop_r(Base);                            /* 出错时发送read停止信号 */
            Base->I2CCON &= ~(1<<4);                            /* 清标志位 */
            return i2c_check_and_clear_error(Base);
        }
    }

    i2c_master_stop_r(Base);                                    /* 发送read停止信号 */
    Base->I2CCON &= ~(1<<4);                                    /* 清标志位 */
    return I2C_STATUS_OK;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值