请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
部件号:MSP430F5529 工具/软件:Code Composer Studio
我在过去的几天里一直在使用MSP430F5529启动板作为测试板深入了解I2C。 我通常使用SPI和UART进行数据传输,但我最近启动了一个需要I2C的项目。 用户指南很好地概述了协议和时间要求,但我觉得它没有准确解释何时需要写入寄存器(以及写入顺序)以便传输成功。
如果您对我早期的奋斗感兴趣,请参阅以下论坛帖子: e2e.ti.com/.../214.3085万
请注意,我尚未完全完成这段代码(超时,压力测试等)。 这是我的车手,可以随时提出建议。 我总是很兴奋地学习更多知识,并帮助那些经历过同样困难的人。
头文件
/*
* i2c.h
*
*创建时间:2017年3月20日
* 作者:Sheldon
*/
#ifndef I2C_H_
#define I2C_H_
#include "stdint.h"
class i2c
{
private:
volatile uint8_t *UCxxRXBUF;
volatile uint8_t *volatile xxTXBUF;UCUint8_t
*UCxxBR1;
volatile uint8_t *UCxxBR0;
lvolatile
volatile uint8_t *UCxxCTL0;
volatile uint8_t *UCxxIFG;
volatile uint8_t *UCxxIE;
volatile uint16_t *UCxxI2CSA;
virtual void pinSetup(); // Super类负责正确设置公共引脚
:
typedef enumI2C_Type_t
{
USCIB0,
USCIB1,
USCIB2
}I2C_Type;
// 创建I2C主接口
i2c (I2C_Type port, uint8_t clkDiv);
~i2c();
bool Byte (uint8_t address, uint*);
bool writeByte (uint8_t地址,uint8_t字节);
uint8_t readReg (uint8_t slaveAdd,uint8_t regAdd,uint8_t* data, uINT8_t len);
};
#endif /* I2C_H_*/
源文件
/*
* i2c.cpp
*
*创建时间:2017年3月20日
* 作者:Sheldon
*/
#include "i2c.h"
#include "MSP40.0."
i2c::i2c (I2C_Type port, uint8_t clkDiv)
{
SWITCH(((Int16_t)port)
{
Case USCIB0:
UCxxCTL1 = &UCB0CTL1;
UCxxCTL0 = UCB0CTR1;UCB0CT0
=
UBR0;UCBI0 = UBR0;UBR0
UCxxIFG =&UCB0IFG;
UCxxRXBUF =&UCB0RXBUF;
UCxxTXBUF =&UCB0TXBUF;
Break;
Case USCIB1:
UCxxCTL1 =&UCB1CTL1;
UCxxCTL0 =&UCB1CTL0;
UCxxBR0 = CBI1BR1
=
UCxxIFG =&UCB1IFG;
UCxxRXBUF =&UCB1RXBUF;
UCxxTXBUF =&UCB1TXBUF;
中断;
默认:
返回
;}
pinSetup();
*UCxxCTL1 || UCSWRST; //启用软件重置
*UCxxCTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C主控,同步模式
*UCxxCTL1 = UCSSEL_2 + UCSSWRST; //使用SMCLK,keep SW reset
*UCxxBR0 = clkDiv; //将时钟分配器应用到SMCLK
*UCxxBR1 =0;
*UCxxCTL1 &=~UCSWRST; //清除软件重置,恢复操作
*UCxxIE =0;
*UCxxIFG =0; //清除中断标志
}
i2c:~i2c ()
{
}
void i2c::pinSetup()
{
#define ACC_I2C_SDA_PIN BIT0
#define ACC_I2C_SDA_OUT P3SEL
#define ACC_IR_SDA_DIR P3DIC_OUT_P3SEL
#define #define
ACC_I2C_REN_I2C_REB3SCL=
//初始化I2C线路的GPIO功能
ACC_I2C_SDA_SEL &=~ACC_SDA_SDA_PIN;
ACC_I2C_SCL_SEL &=~ ACC_I2C_SCL_PIN;
//确保将DIR设置为高电
阻器以防止上/下拉//电阻器启用 (参见用户指南中的REN设置) ACC_I2C_DIC_DIC_DIR_SCL_PIN
~ACC_I2C_ACC_I2C_PIN
~
ACC_I2C_SDA_OUT ||~ACC_I2C_SDA_PIN;
ACC_I2C_SCL_OUT ||~ACC_I2C_SCL_PIN;
ACC_I2C_SDA_OUT =~ACC_I2C_SDA_PIN;
ACC_I2C_SCL_OUT ==~ACC_I2C_SCL_PIN;
__Delay_Cycles (1000);
ACC_ACC_SDA_DIR &=~ACC_I2C_I2C_I2C_PIN;
ACC_I2C_SCL_DIR &=~ACC_I2C_SCL_PIN;
__Delay_Cycles (1000);
//将引脚功能分配给I2C
ACC_I2C_SDA_SEL || ACC_I2C_SDA_PIN;
ACC_I2C_SCL_SEL || ACC_I2C_SCL_PIN;
}
布尔i2c::readByte (uint8_t地址,uint8_t*字节)
{//
问题重复启动条件(SDA高-> SCL高时低转换)
//按下启动条件后,也将按下7位从属地址+ R/W位(UCTR )。
//请参见下面的时间图://#####################################################.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.
//#位#| 1 | 7. | 1 | 1 |#
//#----------- ------------------------------- #//#
Master #| start | slave address | R/W | #//#-----------
------------------------------- #//#
从属# | ACK|#//##########################################
*UCxxI2CSA =地址;
*UCxxCTL1 || UCTXSTT;
*UCxxCTL1 &=~UCTR;
//等待启动条件清除
,同时(*UCxxCTL1和UCTXSTT)== UCTXSTT); //使用计时器执行机具超时
//确保从从属设备接收到确认
//(如果我们没有收到ACK,将设置一个nack中断位)
IF (*UCxxIFG & UCNACKIFG)== UCNACKIFG)
{//
如果收到Nack,发出停止条件
*UCxxCTL1;|| UCTXSTP;
//不从从属设备读取任何字节返回
//警告:如果总线锁定
返回错误,请考虑重置从属设备;
}//
此时从属设备已确认启动条件+ ReadBit,并正在通过总线推动数据字节。
//由于此函数只接收到一个字节,因此我们必须在此处发出停止条件,以确保从属设备不会试图
//发送更多数据。
*UCxxCTL1 || UCTXSTP;
//等待接收数据中断触发
//从从属设备检索到数据后,USCI模块将推出一个停止位
,同时(!(*UCxxIFG & UCRXIFG)); //使用计时器执行超时
//现在我们可以从XBUF加载从属数据字节
*byte =*UCxxXBUF;
//现在我们必须检查从属设备是否已确认我们的停止位
。而(*UCxxCTL1 & UCTXSTP)=UCTXSTP); //使用计时器执行超时
//向成功接收数据的调用函数发送信号
返回true;
}
布尔i2c::writeByte (uint8_t地址,uint8_t字节)
{//
问题重复启动条件(SDA高-> SCL高时低转换)
//按下启动条件后,也将按下7位从属地址+ R/W位(UCTR )。
//请参见下面的时间图://#####################################################.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.
//#位#| 1 | 7. | 1 | 1 |#
//#----------- ------------------------------- #//#
Master #| start | slave address | R/W | #//#-----------
------------------------------- #//#
从属# | ACK|#//##########################################
*UCxxI2CSA =地址;
*UCxxCTL1 || UCTXSTT;
*UCxxCTL1 || UCTR;
//确保从从属设备收到确认
//(如果我们没有收到ACK,将设置一个nack中断位)
if(*UCxxIFG & UCNACKIFG)== UCNACKIFG)
{//
如果收到Nack,发出停止条件
*UCxxCTL1 || UCTXSTP;
//不从从属设备读取任何字节返回
//警告:如果总线锁定
返回false
,则考虑重置从属设备;}//
使用传出字节加载TXBuf
*UCxxTXBUF = byte;
//等待启动条件清除
while (*UCxxCTL1和UCTXSTT)== UCTXSTT); //使用计时器执行机具超时
//等待TXBUF移出
,同时(!(* UCxxIFG和UCTXIFG));
//发出停止条件
* UCxxCTL1 ||= UCTXSTP;
//我们现在必须检查从属设备是否已确认我们的停止位
。同时(* UCxxCTL1和UCTXSTP)= UCTXSTP); //使用计时器执行超时
//向成功传输数据的调用函数发送信号
返回true;
}
uint8_t i2c::readReg (uint8_t slaveAdd, uint8_t regAdd, uint8_t* data, uint8_t len)
{//
操作理论: 许多I2C从属设备都有一个与
地址和值对应的寄存器表。 此函数通过
执行以下操作来读取寄存器(或支持多寄存器读取的设备//)的值:
// 1)发送从属I2C地址
//2)发送从属寄存器地址
//3)使用读取位设置发送重复启动
// 4)读取任意数量的字节
//使用写入启用的第一个发送启动条件
*UCxxI2CSA = slaveAdd;
*UCxxCTL1 |= UCTXSTT;
*UCxxCTL1 || UCTR;
//使用传出字节加载TXBuf
*UCxxTXBUF = regAdd;
//如果我们等待字节移出,即检查TXIFG
//变低,我们就错过了重复启动的机会。
//因此,我们将重复启动,然后等待
//启动条件清除。
//重复启动
*UCxxCTL1 &=~UCTR;
*UCxxCTL1 |= UCTXSTT;
//等待启动条件清除
while (*UCxxCTL1 & UCTXSTT)== UCTXSTT); //使用定时器执行机具超时
//此时从属设备已确认启动条件+ ReadBit,并正在通过总线推送数据字节。
for (uint8_t idx = 0;idx < len; IDX++)
{//
在我们预期接收的最后一个字节之前发出停止条件
IF (idx == len -1)
*UCxxCTL1 || UCTXSTP;
//等待接收数据中断触发
//从从属设备检索到数据后,USCI模块将推出一个停止位
,同时(!(*UCxxIFG & UCRXIFG)); //使用计时器执行超时
//现在我们可以从XBUF
数据[idx]=*UCxxRXBUF;
}//加载从属数据字节
,我们现在必须检查从属设备是否已确认我们的停止位
。while (*UCxxCTL1 & UCTXSTP)=UCTXSTP);//要使用计时器执行超时,返回TRUE