IIC总线特点:
1. 二线传输;
2. 无中心主机;
3. 软件寻址;
4. 应答式数据传输过程;
5. 节点可带点接入或撤出;
6. IIC的SCL和SDA都需要接上拉电阻,保证空闲状态的稳定性;
数据传输过程:
由主机发出起始信号和停止信号。
起始信号:当SCL保持为高电平时,SDA产生一个下降沿,则代表起始信号;
停止信号:当SCL保持为高电平时,SDA产生一个上升沿,则代表停止信号;
数据传输:
SCL的下降沿后 ----> 发送方发送数据位;
SCL的上升沿后SDA总线数据稳定 ----> 接收方接收数据位(因为上升沿后,SCL为高电平,SDA不能有任何电平跳变,只能接收数据);
应答信号:发送方释放SDA总线。若SDA保持低电平,代表接收方发送了一个应答位并拉低了SDA总线;因为默认情况下,SDA为高电平。否则,代表接收方未应答。
注:IIC信号在数据传送的过程中,当SCL=1时,数据线SDA必须保持稳定状态,不允许有电平跳变,否则都会被为是总线的起始信号或者停止信号。只有在时钟线上的信号为低电平期间,数据线上的电平状态才允许变化,所以在代码中会经常看到对SCL引脚置0的操作。
代码实现(通用):
- 主机发送数据
//主机向从机写数据(一个字节)
void IIC_Write_One_Byte(I2C_n i2cn,uint8_t daddr,uint8_t addr,uint8_t data)
{
IIC_Start(i2cn); //1.主机产生一个开始条件
IIC_Send_Byte(i2cn,daddr<<1); //2.主机发送从机地址和方向。前7位为从机地址,第8位表示方向:0表示知己发送数据,1表示主机接收数据
IIC_Wait_Ack(i2cn);//等待接收方应答
IIC_Send_Byte(i2cn,addr); //3.发送从机内部寄存器地址
IIC_Wait_Ack(i2cn);
IIC_Send_Byte(i2cn,data); //4.发送数据字节
IIC_Wait_Ack(i2cn);
IIC_Stop(i2cn);//5.主机产生一个停止条件
DELAY_US(100);
}
- 主机接收数据
//主机从从机读数据(一个字节)
uint8_t IIC_Read_One_Byte(I2C_n i2cn,uint8_t daddr,uint8_t addr)
{
uint8_t temp=0;
IIC_Start(i2cn); //1.主机产生一个开始条件
IIC_Send_Byte(i2cn,daddr<<1);//2.主机发送从机地址和方向,暂时主机需要向从机发送从机内部寄存器地址,所以最后一位任然是0
IIC_Wait_Ack(i2cn);
IIC_Send_Byte(i2cn,addr); //3.发送从机内部寄存器地址
IIC_Wait_Ack(i2cn);
IIC_Start(i2cn);
IIC_Send_Byte(i2cn,(daddr<<1)|0x01); //4.前面主机需要发送的数据已经发送完了,之后进入接收模式,所以最后一位发送1
IIC_Wait_Ack(i2cn);
temp=IIC_Read_Byte(i2cn,0);
IIC_Stop(i2cn);//5.产生一个停止条件
return temp;
}
MSP430F5529的IIC配置:
- 基本过程
(官方文档的Users Guide)
The recommended USCI initialization/reconfiguration process is:
-
Set UCSWRST (BIS.B #UCSWRST,&UCxCTL1). 设置UCSWRST位
-
Initialize all USCI registers with UCSWRST = 1. 在UCSWRST位为1的条件下,初始化其他寄存器
-
Configure ports. 配置引脚
-
Clear UCSWRST through software (BIC.B #UCSWRST,&UCxCTL1). 清除UCSWRST位
-
Enable interrupts (optional).使能中断(可选)
- 代码实现
void I2C_MasterInit(I2Cn i2cn,uint16_t SlaveID,uint32_t BaudRate)
{
WordType BR;
BR.Word=g_sClock.SMCLK.nHZ/BaudRate; //求取波特率所需的分频系数
GPIO_Init(I2C_PIN[i2cn-I2C0].SCL.Port,I2C_PIN[i2cn-I2C0].SCL.Pin,GPO);
//输出9个时钟以恢复I2Cn总线状态
for(uint8_t i=0;i<9;i++)
{
GPIO_WriteBit (I2C_PIN[i2cn-I2C0].SCL.Port,I2C_PIN[i2cn-I2C0].SCL.Pin, BIT_SET);
DELAY_US(5);
GPIO_WriteBit (I2C_PIN[i2cn-I2C0].SCL.Port,I2C_PIN[i2cn-I2C0].SCL.Pin,RESET);
DELAY_US(5);
}
//初始化引脚
//3. Configure ports.
GPIO_Init(I2C_PIN[i2cn-I2C0].SCL.Port,I2C_PIN[i2cn-I2C0].SCL.Pin,SEL);
GPIO_Init(I2C_PIN[i2cn-I2C0].SDA.Port,I2C_PIN[i2cn-I2C0].SDA.Pin,SEL);
//初始化寄存器
//1. Set UCSWRST (BIS.B #UCSWRST,&UCxCTL1).
USCIX[i2cn]->CTL1 = UCSWRST; // 软件复位使能,保持复位状态
//2. Initialize all USCI registers with UCSWRST = 1.
USCIX[i2cn]->CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C主机,同步模式
if(SlaveID > 0x7F)
{
USCIX[i2cn]->SLA10 = BIT_SET; //10-bit address
}
else
{
USCIX[i2cn]->SLA10 = RESET; //7-bit address
}
USCIX[i2cn]->I2CSA = SlaveID; //从机地址寄存器 赋值
USCIX[i2cn]->CTL1 |= UCSSEL__SMCLK; //使用SMCLK作为时钟源
USCIX[i2cn]->BR0 = BR.Byte[0];
USCIX[i2cn]->BR1 = BR.Byte[1];
//4. Clear UCSWRST through software (BIC.B #UCSWRST,&UCxCTL1).
USCIX[i2cn]->CTL1 &=~ UCSWRST; //清除软件复位,正常操作
//5. Enable or disable interrupts (optional)
USCIX[i2cn]->IE = 0u; //关闭所有中断
USCIX[i2cn]->RXIFG = RESET; //清除接收数据标志
USCIX[i2cn]->TXIFG = BIT_SET; //置位发送缓冲区为空标志
}
- 代码解析
1、MSP430的I2C有一个BUSY位:
The bus busy bit, UCBBUSY, is set after a START and cleared after a STOP
BUSY位为1或者UCTCSTP位为1时,代表I2C总线忙。
//代码实现如下:
while((USCIX[i2cn]->UC_BUSY==BIT_SET) || (USCIX[i2cn]->TXSTP==BIT_SET));//确保总线空闲
//起始位发送:
inline void I2C_Start(I2Cn i2cn)
{
while((USCIX[i2cn]->UC_BUSY==BIT_SET) || (USCIX[i2cn]->TXSTP==BIT_SET));//确保总线空闲
USCIX[i2cn]->TXSTT = BIT_SET;
}
//停止位发送:
inline void I2C_Stop(I2Cn i2cn)
{
while(USCIX[i2cn]->UC_BUSY == BIT_SET); //等待空闲
/*
TXSTP位是一个条件位,该条件在NACK之前:
当该位为1时,代表需要产生一个STOP位,在STOP产生后,会自动清除该位。
于是就有了下面两行代码。
*/
USCIX[i2cn]->TXSTP =BIT_SET; //发送停止位
while(USCIX[i2cn]->TXSTP == BIT_SET); //等待停止位发送完成
}
/*******************************************************************************
* 函数名称:I2C_WaitBusy(I2Cn i2cn)
* 功能说明:I2C等待空闲
* 参数说明:I2Cn i2cn :模块号
* 函数返回:无
* 使用示例:I2C_WaitBusy(I2C1); //等待I2C1模块不忙
********************************************************************************/
inline void I2C_WaitBusy(I2Cn i2cn)
{
while(USCIX[i2cn]->UC_BUSY == BIT_SET); //等待发送或接收完成
}
2、模式切换
inline void I2C_EnterSend (I2Cn i2cn)
{
USCIX[i2cn]->TR = BIT_SET; //进入发送模式
}
inline void I2C_EnterRead (I2Cn i2cn)
{
USCIX[i2cn]->TR = RESET; //进入接收模式
}
3、开发板内部操作,并未涉及到主从之间的信息传递:发送/接收一字节
/*******************************************************************************
* 函数名称:I2C_SendByte (I2Cn i2cn,uint8_t data)
* 功能说明:I2C发送一字节数据
* 参数说明:I2Cn i2cn :模块号
uint8_t data :要发送的数据
* 函数返回:无
* 使用示例:I2C_SendByte (I2C1,0x01); //I2C1模块发送一字节数据0x01
********************************************************************************/
inline void I2C_SendByte (I2Cn i2cn,uint8_t data)
{
/*
当UCBxTXBUF寄存器为空时,TXIFG位被置为1
*/
while(USCIX[i2cn]->TXIFG == RESET); //等待TXBUF为空
USCIX[i2cn]->TXBUF = data; //发送要写入的数据
}
/*******************************************************************************
* 函数名称:I2C_ReadByte (I2Cn i2cn)
* 功能说明:IIC读取一个字节数据
* 参数说明:I2Cn i2cn :模块号
* 函数返回:读取到的数据
* 使用示例:uint8 data = I2C_ReadByte (I2C1); //读取数据
********************************************************************************/
inline uint8_t I2C_ReadByte (I2Cn i2cn)
{
/*
当UCBxRXBUF寄存器接收到一个完整的字节时,RXIFG位被置为1
*/
while(USCIX[i2cn]->RXIFG == RESET); //等待接收到数据
return USCIX[i2cn]->RXBUF;
}
4、Data on SDA must be stable during the high period of SCL (see Figure 38-4). The high and low state of SDA can only change when SCL is low, otherwise START or STOP conditions are generated.
下面这种时序对于数据传输过程才是安全的,即在SCL为高电平时,SDA总线的电平不发生改变;数据的接收和发送(SDA的边沿处)总是发生在SCL为低电平时。
5、主从机之间通信
/*******************************************************************************
* 函数名称: I2C_WriteReg(I2Cn i2cn, uint8_t address, char data)
* 功能说明: 往某一个地址写入一字节数据
* 参数说明: I2Cn i2cn :模块号
uint8_t address :寄存器地址
uint8_t data :对该地址要写入的数据内容
* 函数返回:无
* 使用示例:I2C_WriteReg(I2C1, 0x20, 0x12); //对地址为0x20处写入内容0x12
********************************************************************************/
void I2C_WriteReg(I2Cn i2cn, uint8_t address, uint8_t data)
{
I2C_EnterSend(i2cn);
I2C_Start (i2cn); //发送一个起始信号
I2C_SendByte (i2cn,address); //发送要写入的地址
I2C_SendByte (i2cn,data); //发送要写入的数据
I2C_Stop (i2cn); //发送停止位
I2C_WaitBusy (i2cn);
}
/*******************************************************************************
* 函数名称: I2C_ReadReg(I2Cn i2cn, uint8_t address)
* 功能说明: 对外部芯片读取某一地址的内容
* 参数说明: I2Cn i2cn :模块号
uint8_t address :寄存器地址
* 函数返回: 读取到的内容
* 使用示例: uint8_t data = I2C_ReadReg(I2C1, 0x20); //读取寄存器地址为0x20处的内容
********************************************************************************/
uint8_t I2C_ReadReg(I2Cn i2cn, uint8_t address)
{
I2C_EnterSend (i2cn); //进入发送模式
I2C_Start (i2cn); //发送一个起始信号
I2C_SendByte (i2cn,address); //发送一字节数据
I2C_WaitBusy (i2cn); //等待传输完毕
I2C_EnterRead (i2cn); //进入接收模式
I2C_Start (i2cn); //发送一个起始信号
I2C_WaitBusy (i2cn); //等待空闲
I2C_Stop (i2cn); //发送一个停止信号,读的话要先发送停止位
return I2C_ReadByte(i2cn); //读取数据
}
/*
注:
1、对比这里的代码和上面的通用代码,就会发现这里少了发送从机地址的步骤,我没有找到具体的描述文字,猜测应该是将从机地址存入对应的寄存器且指定模式(发送或接收模式)后,MSP430内部有自动找到正确从机的机制。
2、跟通用代码还有个不一样的点,就是MSP430的I2C数据接收是在发送STOP位之后。官方文档中有这么一句:If UCBxRXBUF is not read, the master holds the bus during reception of the last data bit and until the UCBxRXBUF is read. 猜测大概意思是,RXBUF中的数据如果没有被读出去的话,主机应该会保持SCL总线的电平,直到数据被读取。
*/
6、连续写/读文章来源:https://www.toymoban.com/news/detail-648491.html
//IIC主机连续写
//i2cn:IIC模块号
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
// 其他,错误代码
char I2C_Write_Len(I2Cn i2cn,uint8_t reg,uint8_t len,uint8_t *buf)
{
uint8_t i;
I2C_EnterSend (i2cn);
I2C_Start(i2cn);
I2C_SendByte(i2cn,reg); //写寄存器地址
I2C_WaitBusy(i2cn); //等待应答
for(i=0;i<len;i++)
{
I2C_SendByte(i2cn,buf[i]); //发送数据
I2C_WaitBusy(i2cn);
}
I2C_Stop(i2cn);
return 0;
}
//IIC主机连续读
//i2cn:IIC模块号
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
// 其他,错误代码
uint8_t I2C_Read_Len(I2Cn i2cn,uint8_t reg,uint8_t len,uint8_t *buf)
{
I2C_EnterSend (i2cn);
I2C_Start(i2cn);
I2C_SendByte(i2cn,reg); //写寄存器地址
I2C_WaitBusy(i2cn); //等待应答
I2C_EnterRead (i2cn);
I2C_Start(i2cn);
while(len)
{
if(len==1)*buf=I2C_ReadByte(i2cn);//读数据,发送nACK
else *buf=I2C_ReadByte(i2cn); //读数据,发送ACK
len--;
buf++;
}
I2C_Stop(i2cn); //产生一个停止条件
return 0;
}
7、其他文章来源地址https://www.toymoban.com/news/detail-648491.html
/*******************************************************************************
* 函数名称: I2C_ITConfig (I2Cn i2cn,I2C_IRQn irqn,STATUS ITState)
* 功能说明: 设置使能或禁止I2Cn的某一个中断
* 参数说明: I2Cn i2cn :模块号
I2C_IRQn irqn :中断类型
* 函数返回:无
* 使用示例:I2C_ITConfig (I2C1,I2C_RX_IRQn,ENABLE); //使能I2C1的接收中断
********************************************************************************/
void I2C_ITConfig (I2Cn i2cn,I2C_IRQn irqn,STATUS ITState)
{
if(ITState != DISABLE)
{
USCIX[i2cn]->IE |= irqn;
}
else
{
USCIX[i2cn]->IE &=~irqn;
}
}
/*******************************************************************************
* 函数名称: I2C_GetITStatus(I2Cn i2cn,I2C_IRQn irqn)
* 功能说明: 获取I2C的某一个中断标志
* 参数说明: I2Cn i2cn :模块号
I2C_IRQn irqn :中断类型
* 函数返回: STATUS : TRUE 中断事件发生,FALSE 中断事件未发生
* 使用示例: if(TRUE == I2C_GetITStatus(I2C0,I2C_RX_IRQn)){...} //判断I2C0模块是否接收完成事件中断发生
********************************************************************************/
STATUS I2C_GetITStatus(I2Cn i2cn,I2C_IRQn irqn)
{
return ((USCIX[i2cn]->IFG & irqn) ? TRUE :FALSE);
}
/*******************************************************************************
* 函数名称: I2C_ClearITPendingBit(I2Cn i2cn,I2C_IRQn irqn)
* 功能说明: 清除I2Cn的某一个中断标志
* 参数说明: I2Cn i2cn :模块号
I2C_IRQn irqn :中断类型
* 函数返回: 无
* 使用示例: I2C_ClearITPendingBit(I2C0,I2C_RX_IRQn); //清除I2C1模块接收中断标志位
********************************************************************************/
void I2C_ClearITPendingBit(I2Cn i2cn,I2C_IRQn irqn)
{
USCIX[i2cn]->IFG &=~ irqn;
}
到了这里,关于I2C用法和MSP430F5299上的I2C的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!