IMX6ULL裸机开发之硬件IIC分析及配置过程

裸机配置硬件I2C

关于I2C的具体介绍,可以参考我的另一篇文章

STM32之IIC详细解析

IMX6U的硬件I2C特性

  1. 兼容标准I2C总线;
  2. 软件可配置64个不同的串行时钟频率;
  3. 软件可选的应答信号;
  4. start/end信号生成和检测;
  5. 重复start信号生成;
  6. 确认位生成;
  7. 总线busy检测。

I2C的两种模式

  • 标准模式:在该模式下I2C传输速率可达100kb/s;
  • 高速模式:在该模式下I2C传输速率可达400kb/s;

外部引脚MAP

IMX6U有4组IIC,这里举个例子,另外的参考《IMX6U参考手册》P1244
在这里插入图片描述

时钟

IIC的时钟源有两个:

(一个用于读写操作的模块时钟,一个是I2C的功能时钟。串行位时钟频率来自于模块时钟。模块时钟和外围时钟相互同步。模块时钟的最小频率应为12.8MHz,以达到400kbps的操作。)

在这里插入图片描述

硬件I2C概述图

从上到下由地址/数据块,5个寄存器,时钟块组成。这样的block,IMX6U有四个。
在这里插入图片描述

寄存器介绍

每组IIC对应5个寄存器,下图是IIC1的寄存器组
在这里插入图片描述

I2Cx_IFDR寄存器

该寄存器用于配置IIC的时钟,但是不能够随意配置的,而是有固定的值。该寄存器只有低6位有效。
在这里插入图片描述

I2Cx_I2CR寄存器

在这里插入图片描述
IEN:I2C 使能位,1使能,0关闭
IIEN:I2C 中断使能位,1使能,0关闭
MSTA:主从模式选择位,1主模式,0从模式
MTX:传输方向选择位,1接收,0发送
TXAK:传输应答位使能,1发送无应答信号NACK,0发送应答信号ACK
RSTA:重复开始信号,1发送一个restart信号

I2Cx_I2SR寄存器

在这里插入图片描述
ICF:数据传输状态位,0传输完成,1正在传输
IAAS:为1则表示IIC地址
IBB:总线BUSY位,0空闲,1忙
IAL:仲裁丢失位,为1则发生仲裁丢失
SRW:从机读写状态位(IIC做从机时使用)
IIF:中断挂起标志位,要软件清零。
RXAK:应答信号标志位

代码分析

IIC初始化

void i2c_io_init(void)
{
    //配置I2C1的相关引脚
    IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);
	IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);
    IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x70B0);
	IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0X70B0);
}

void i2c_init(I2C_Type *base)
{
	base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C */
	base->IFDR = 0X15 << 0;	 /* 配置分频系数,以设置I2C波特率为100K */
	base->I2CR |= (1<<7);	 /* 使能IIC设备 */
}

配置IIC的start信号

unsigned char i2c_start(I2C_Type *base, unsigned char address,  enum i2c_direction direction)
{
	if(base->I2SR & (1 << 5))	/* 检测I2C是否忙 */
		return 1;
	base->I2CR |=  (1 << 5) | (1 << 4);	/* 配置IIC为主模式,数据传输方向为发送数据 */

    /* 设置从机地址 */
	base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
	return 0;
}

配置IIC的restart信号

unsigned char i2c_repeated_start(I2C_Type *base, unsigned char address,  enum i2c_direction direction)
{
	base->I2CR |=  (1 << 4) | (1 << 2); /* 配置IIC为发数据方向并产生一个restart信号 */
	/* 设置从机地址 */
	base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
	return 0;
}

配置IIC的停止信号

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;
}

配置发送和接收函数

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);			/* 清除标志位 */
		if(i2c_check_and_clear_error(base, base->I2SR))	/* 检查有无ACK */
			break;
	}
	base->I2SR &= ~(1 << 1);
	i2c_master_stop(base); 	/* 发送停止信号 */
}


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));	/* 接收数据 */
	if(size == 1)				/* 如果只接收一个字节数据的话发送NACK信号 */
        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;
	}
}
  • 有了以上的几个函数,所有的IIC传输数据的模块就集齐了,接下来就是将这些模块组装在一起,构成IIC的传输函数。

传输函数

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--;				/* 地址长度减一 */
			
            //向I2DR寄存器写入子地址
            base->I2DR =  ((xfer->subaddress) >> (8 * xfer->subaddressSize)); 

            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;	
}

头文件

#include "imx6ul.h"

/* 相关宏定义 */
#define I2C_STATUS_OK				(0)
#define I2C_STATUS_BUSY				(1)
#define I2C_STATUS_IDLE				(2)
#define I2C_STATUS_NAK				(3)
#define I2C_STATUS_ARBITRATIONLOST	(4)
#define I2C_STATUS_TIMEOUT			(5)
#define I2C_STATUS_ADDRNAK			(6)


/*
 * I2C方向枚举类型
 */
enum i2c_direction
{
    kI2C_Write = 0x0, 		/* 主机向从机写数据 */
    kI2C_Read = 0x1,  		/* 主机从从机读数据 */
} ;

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


/*
 *函数声明
 */
void i2c_init(I2C_Type *base);
unsigned char i2c_master_start(I2C_Type *base, unsigned char address, enum i2c_direction direction);
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address,  enum i2c_direction direction);
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status);
unsigned char i2c_master_stop(I2C_Type *base);
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size);
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size);
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer);


#endif
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你想要学习在IMX6ULL平台上裸机(即不依赖操作系统)编程控制LCD吗?这是一个不错的学习项目!在开始之前,你需要了解一些基本的知识,包括LCD的工作原理、接口协议以及IMX6ULL平台的GPIO控制等。以下是一些步骤供你参考: 1. 了解LCD的基本知识:学习LCD的工作原理、显示原理、分辨率、颜色深度等。 2. 确定LCD的接口类型:IMX6ULL平台可能支持不同的接口类型,如RGB、LVDS、MIPI等,你需要确定你使用的LCD的接口类型,并准备相应的连接线。 3. 配置IMX6ULL的GPIO:通过配置IMX6ULL的GPIO引脚,将其连接到LCD的控制和数据线上。你可以查阅IMX6ULL的技术文档或参考资料来了解如何配置GPIO。 4. 编写LCD控制代码:使用C语言或汇编语言编写裸机代码,通过设置GPIO引脚的电平和发送相应的命令和数据来控制LCD。你需要根据LCD的接口协议编写相应的代码。 5. 调试和测试:将代码下载到IMX6ULL开发板上,连接LCD,并进行调试和测试。你可以使用示波器或逻辑分析仪来检查信号是否正确发送到LCD,并观察LCD是否正确显示图像。 请注意,裸机编程需要一定的硬件和低级编程知识。确保在开始之前对相关的知识和技术有一定的了解。另外,IMX6ULL平台可能有其特定的文档和资料,你可以查阅相关文档以获取更详细的信息。祝你成功学习LCD控制!如果你有其他问题,欢迎继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值