
How To Run: see TencentOS-tiny\board\ALPHA_I.MX_emmc_256ddr\README.md TODO Next: 1. VFP support 2. fault diagnosis support 3. qemu vexpress ca9 support 4. raspberry pi support 5. SMP support
316 lines
8.5 KiB
C
316 lines
8.5 KiB
C
/***************************************************************
|
||
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
|
||
文件名 : bsp_i2c.c
|
||
作者 : 左忠凯
|
||
版本 : V1.0
|
||
描述 : IIC驱动文件。
|
||
其他 : 无
|
||
论坛 : www.openedv.com
|
||
日志 : 初版V1.0 2019/1/15 左忠凯创建
|
||
***************************************************************/
|
||
#include "bsp_i2c.h"
|
||
#include "bsp_delay.h"
|
||
#include "printf.h"
|
||
|
||
/*
|
||
* @description : 初始化I2C,波特率100KHZ
|
||
* @param - base : 要初始化的IIC设置
|
||
* @return : 无
|
||
*/
|
||
void i2c_init(I2C_Type *base)
|
||
{
|
||
/* 1、配置I2C */
|
||
base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C */
|
||
|
||
/* 设置波特率为100K
|
||
* I2C的时钟源来源于IPG_CLK_ROOT=66Mhz
|
||
* IC2 时钟 = PERCLK_ROOT/dividison(IFDR寄存器)
|
||
* 设置寄存器IFDR,IFDR寄存器参考IMX6UL参考手册P1260页,表29-3,
|
||
* 根据表29-3里面的值,挑选出一个还是的分频数,比如本例程我们
|
||
* 设置I2C的波特率为100K, 因此当分频值=66000000/100000=660.
|
||
* 在表29-3里面查找,没有660这个值,但是有640,因此就用640,
|
||
* 即寄存器IFDR的IC位设置为0X15
|
||
*/
|
||
base->IFDR = 0X15 << 0;
|
||
|
||
/*
|
||
* 设置寄存器I2CR,开启I2C
|
||
* bit[7] : 1 使能I2C,I2CR寄存器其他位其作用之前,此位必须最先置1
|
||
*/
|
||
base->I2CR |= (1<<7);
|
||
}
|
||
|
||
/*
|
||
* @description : 发送重新开始信号
|
||
* @param - base : 要使用的IIC
|
||
* @param - addrss : 设备地址
|
||
* @param - direction : 方向
|
||
* @return : 0 正常 其他值 出错
|
||
*/
|
||
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address, enum i2c_direction direction)
|
||
{
|
||
/* I2C忙并且工作在从模式,跳出 */
|
||
if(base->I2SR & (1 << 5) && (((base->I2CR) & (1 << 5)) == 0))
|
||
return 1;
|
||
|
||
/*
|
||
* 设置寄存器I2CR
|
||
* bit[4]: 1 发送
|
||
* bit[2]: 1 产生重新开始信号
|
||
*/
|
||
base->I2CR |= (1 << 4) | (1 << 2);
|
||
|
||
/*
|
||
* 设置寄存器I2DR
|
||
* bit[7:0] : 要发送的数据,这里写入从设备地址
|
||
* 参考资料:IMX6UL参考手册P1249
|
||
*/
|
||
base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* @description : 发送开始信号
|
||
* @param - base : 要使用的IIC
|
||
* @param - addrss : 设备地址
|
||
* @param - direction : 方向
|
||
* @return : 0 正常 其他值 出错
|
||
*/
|
||
unsigned char i2c_master_start(I2C_Type *base, unsigned char address, enum i2c_direction direction)
|
||
{
|
||
if(base->I2SR & (1 << 5)) /* I2C忙 */
|
||
return 1;
|
||
|
||
/*
|
||
* 设置寄存器I2CR
|
||
* bit[5]: 1 主模式
|
||
* bit[4]: 1 发送
|
||
*/
|
||
base->I2CR |= (1 << 5) | (1 << 4);
|
||
|
||
/*
|
||
* 设置寄存器I2DR
|
||
* bit[7:0] : 要发送的数据,这里写入从设备地址
|
||
* 参考资料:IMX6UL参考手册P1249
|
||
*/
|
||
base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* @description : 检查并清除错误
|
||
* @param - base : 要使用的IIC
|
||
* @param - status : 状态
|
||
* @return : 状态结果
|
||
*/
|
||
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status)
|
||
{
|
||
/* 检查是否发生仲裁丢失错误 */
|
||
if(status & (1<<4))
|
||
{
|
||
base->I2SR &= ~(1<<4); /* 清除仲裁丢失错误位 */
|
||
|
||
base->I2CR &= ~(1 << 7); /* 先关闭I2C */
|
||
base->I2CR |= (1 << 7); /* 重新打开I2C */
|
||
return I2C_STATUS_ARBITRATIONLOST;
|
||
}
|
||
else if(status & (1 << 0)) /* 没有接收到从机的应答信号 */
|
||
{
|
||
return I2C_STATUS_NAK; /* 返回NAK(No acknowledge) */
|
||
}
|
||
return I2C_STATUS_OK;
|
||
}
|
||
|
||
/*
|
||
* @description : 停止信号
|
||
* @param - base : 要使用的IIC
|
||
* @param : 无
|
||
* @return : 状态结果
|
||
*/
|
||
unsigned char i2c_master_stop(I2C_Type *base)
|
||
{
|
||
unsigned short timeout = 0xffff;
|
||
|
||
/*
|
||
* 清除I2CR的bit[5:3]这三位
|
||
*/
|
||
base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));
|
||
|
||
/* 等待忙结束 */
|
||
while((base->I2SR & (1 << 5)))
|
||
{
|
||
timeout--;
|
||
if(timeout == 0) /* 超时跳出 */
|
||
return I2C_STATUS_TIMEOUT;
|
||
}
|
||
return I2C_STATUS_OK;
|
||
}
|
||
|
||
/*
|
||
* @description : 发送数据
|
||
* @param - base : 要使用的IIC
|
||
* @param - buf : 要发送的数据
|
||
* @param - size : 要发送的数据大小
|
||
* @param - flags : 标志
|
||
* @return : 无
|
||
*/
|
||
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size)
|
||
{
|
||
/* 等待传输完成 */
|
||
while(!(base->I2SR & (1 << 7)));
|
||
|
||
base->I2SR &= ~(1 << 1); /* 清除标志位 */
|
||
base->I2CR |= 1 << 4; /* 发送数据 */
|
||
|
||
while(size--)
|
||
{
|
||
base->I2DR = *buf++; /* 将buf中的数据写入到I2DR寄存器 */
|
||
|
||
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
|
||
base->I2SR &= ~(1 << 1); /* 清除标志位 */
|
||
|
||
/* 检查ACK */
|
||
if(i2c_check_and_clear_error(base, base->I2SR))
|
||
break;
|
||
}
|
||
|
||
base->I2SR &= ~(1 << 1);
|
||
i2c_master_stop(base); /* 发送停止信号 */
|
||
}
|
||
|
||
/*
|
||
* @description : 读取数据
|
||
* @param - base : 要使用的IIC
|
||
* @param - buf : 读取到数据
|
||
* @param - size : 要读取的数据大小
|
||
* @return : 无
|
||
*/
|
||
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size)
|
||
{
|
||
volatile uint8_t dummy = 0;
|
||
|
||
dummy++; /* 防止编译报错 */
|
||
|
||
/* 等待传输完成 */
|
||
while(!(base->I2SR & (1 << 7)));
|
||
|
||
base->I2SR &= ~(1 << 1); /* 清除中断挂起位 */
|
||
base->I2CR &= ~((1 << 4) | (1 << 3)); /* 接收数据 */
|
||
|
||
/* 如果只接收一个字节数据的话发送NACK信号 */
|
||
if(size == 1)
|
||
base->I2CR |= (1 << 3);
|
||
|
||
dummy = base->I2DR; /* 假读 */
|
||
|
||
while(size--)
|
||
{
|
||
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
|
||
base->I2SR &= ~(1 << 1); /* 清除标志位 */
|
||
|
||
if(size == 0)
|
||
{
|
||
i2c_master_stop(base); /* 发送停止信号 */
|
||
}
|
||
|
||
if(size == 1)
|
||
{
|
||
base->I2CR |= (1 << 3);
|
||
}
|
||
*buf++ = base->I2DR;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* @description : I2C数据传输,包括读和写
|
||
* @param - base: 要使用的IIC
|
||
* @param - xfer: 传输结构体
|
||
* @return : 传输结果,0 成功,其他值 失败;
|
||
*/
|
||
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer)
|
||
{
|
||
unsigned char ret = 0;
|
||
enum i2c_direction direction = xfer->direction;
|
||
|
||
base->I2SR &= ~((1 << 1) | (1 << 4)); /* 清除标志位 */
|
||
|
||
/* 等待传输完成 */
|
||
while(!((base->I2SR >> 7) & 0X1)){};
|
||
|
||
/* 如果是读的话,要先发送寄存器地址,所以要先将方向改为写 */
|
||
if ((xfer->subaddressSize > 0) && (xfer->direction == kI2C_Read))
|
||
{
|
||
direction = kI2C_Write;
|
||
}
|
||
|
||
ret = i2c_master_start(base, xfer->slaveAddress, direction); /* 发送开始信号 */
|
||
if(ret)
|
||
{
|
||
return ret;
|
||
}
|
||
|
||
while(!(base->I2SR & (1 << 1))){}; /* 等待传输完成 */
|
||
|
||
ret = i2c_check_and_clear_error(base, base->I2SR); /* 检查是否出现传输错误 */
|
||
if(ret)
|
||
{
|
||
i2c_master_stop(base); /* 发送出错,发送停止信号 */
|
||
return ret;
|
||
}
|
||
|
||
/* 发送寄存器地址 */
|
||
if(xfer->subaddressSize)
|
||
{
|
||
do
|
||
{
|
||
base->I2SR &= ~(1 << 1); /* 清除标志位 */
|
||
xfer->subaddressSize--; /* 地址长度减一 */
|
||
|
||
base->I2DR = ((xfer->subaddress) >> (8 * xfer->subaddressSize)); //向I2DR寄存器写入子地址
|
||
|
||
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
|
||
|
||
/* 检查是否有错误发生 */
|
||
ret = i2c_check_and_clear_error(base, base->I2SR);
|
||
if(ret)
|
||
{
|
||
i2c_master_stop(base); /* 发送停止信号 */
|
||
return ret;
|
||
}
|
||
} while ((xfer->subaddressSize > 0) && (ret == I2C_STATUS_OK));
|
||
|
||
if(xfer->direction == kI2C_Read) /* 读取数据 */
|
||
{
|
||
base->I2SR &= ~(1 << 1); /* 清除中断挂起位 */
|
||
i2c_master_repeated_start(base, xfer->slaveAddress, kI2C_Read); /* 发送重复开始信号和从机地址 */
|
||
while(!(base->I2SR & (1 << 1))){};/* 等待传输完成 */
|
||
|
||
/* 检查是否有错误发生 */
|
||
ret = i2c_check_and_clear_error(base, base->I2SR);
|
||
if(ret)
|
||
{
|
||
ret = I2C_STATUS_ADDRNAK;
|
||
i2c_master_stop(base); /* 发送停止信号 */
|
||
return ret;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
/* 发送数据 */
|
||
if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0))
|
||
{
|
||
i2c_master_write(base, xfer->data, xfer->dataSize);
|
||
}
|
||
|
||
/* 读取数据 */
|
||
if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0))
|
||
{
|
||
i2c_master_read(base, xfer->data, xfer->dataSize);
|
||
}
|
||
return 0;
|
||
}
|
||
|