0.前言

对于大多数单片机来说,I2C成了一个老大难问题。从51时代开始,软件模拟I2C成了主流,甚至到ARMCortex M3大行其道的今天,软件模拟I2C依然是使用最广的方法。虽然软件模拟可以解决所有的问题,但是总感觉没有充分发挥MCU内部的硬件资源。查阅了所有关于MSP430F5系列的图书,没有关于硬件I2C的应用代码,自己通过调试摸索,把经验总结之后和大家分享,希望大家喜欢。同时,I2C的使用可以分为等待法和中断法,从理解的角度来说等待法思路清晰易于上手,从功耗的角度出发,中断法可以灵活的进入低功耗模式,但是不易理解。本文先从等待法入手。

MSP430F5系列的硬件I2C使用大致会有以下问题:

I2C地址设定。一般情况下I2C的7位地址被写成了8位长度,最低位无效。例如AT24C02的I2C地址为0xA0,其实真正的7位地址为0x50。而MSP430正是需要填入这7位地址0x50。

I2C停止位发送。在I2C读操作过程中,读取最后一个字节之后MCU应向从机发送无应答,MSP430F5系列的MCU发送无应答的操作将自动完成,这就以为在读取最后一个字节内容时,应先操作停止位相关寄存器。

I2C起始位发送。如果仔细分析MSP430F5参考手册,将会发现读操作和写操作发送I2C起始位时略有不同。写操作时需要先向TXBUF中写入数据,之后才可以等待TXSTT标志位变为0,而读操作和写操作稍有不同。

1.初始化设置

1.1代码实现

void ucb0_config(void)

{

P3SEL &= ~BIT2;                         // P3.2@UCB0SCL

P3DIR |= BIT2;

P3OUT |= BIT2;

个时钟以恢复I2C总线状态

; i++ )

{

P3OUT |= BIT2;

__delay_cycles);

P3OUT &= ~BIT2;

__delay_cycles);

}

P3SEL |= (BIT1 + BIT2);                 // P3.1@UCB0SDAP3.2@UCB0SCL

// P3.1@ISP.1 P3.2@ISP.5

UCB0CTL1 |= UCSWRST;

UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC ;  // I2C主机模式

UCB0CTL1 |= UCSSEL_2;                   // 选择SMCLK

UCB0BR0 ;

UCB0BR1 ;

UCB0CTL0 &= ~UCSLA10;                   // 7位地址模式

UCB0I2CSA = EEPROM_ADDRESS;            // EEPROM地址

UCB0CTL1 &= ~UCSWRST;

}

1.2代码分析

I2C从设备的地址一般有以下通俗说法——7位地址,写地址(写控制字)和读地址(读控制字)。1个I2C通信的控制字节(I2C启动之后传送的第一个字节)由7位I2C地址和1位读写标志位组成,7位I2C地址即7位地址,若读写标志位为读标志(读写标志位置位)加上7位I2C地址便组成了读地址(读控制字),若读写标志位为写标志(读写标志位清零)加上7位地址便组成了写地址(写控制字)。例如AT24C02的I2C7位地址为0x50,读地址(读控制字)为0xA1,写地址(写控制字)为0xA1。

在MSP430F5系列中,I2CSA地址寄存器应写入位地址,参照上面的例子应写入0X50。至于I2C读写位的控制由CTL1寄存器完成,用户无需干预。

在I2C设置开始之前,可以先通过SCL端口发送9个时钟信号,该时钟信号可以是I2C从机芯片从一种错误的通信状态恢复,虽然这9个时钟信号不起眼但是对于调试过程来说非常有用。例如在调试过程中,错误的发送了停止位,若再次启动调试则I2C从设备仍处于一种错误的状态,这9个时钟信号可以把I2C从设备从错误的状态“拉”回来。

2.写单个字节

向I2C从设备写入单个字节应该是最为简单的一个操作,因为所有的控制权都在主机手中。写单个字节实际包括了2个重要部分,一个便是写寄存器地址,另一个便是写寄存器内容。对于AT24C02而言,存储内容的字节长度为一个字节,而对于容量更大的EEPROM而言,存储地址可为两个字节。

2.1 代码实现

uint8_teeprom_writebyte( uint8_t word_addr , uint8_tword_value )

{

while( UCB0CTL1 & UCTXSTP );

UCB0CTL1 |= UCTR;                 // 写模式

UCB0CTL1 |= UCTXSTT;              // 发送启动位

UCB0TXBUF = word_addr;            // 发送字节地址

// 等待UCTXIFG=1 与UCTXSTT=0 同时变化等待一个标志位即可

while(!(UCB0IFG & UCTXIFG))

{

if( UCB0IFG & UCNACKIFG )       // 若无应答 UCNACKIFG=1

{

;

}

}

UCB0TXBUF = word_value;           // 发送字节内容

while(!(UCB0IFG & UCTXIFG));      // 等待UCTXIFG=1

UCB0CTL1 |= UCTXSTP;

while(UCB0CTL1 & UCTXSTP);        // 等待发送完成

;

}

2.2 代码分析

关于代码出口的说明,关于I2C的读写函数,若返回值为0说明所有的操作正常,若返回值为非0说明操作有误,例如1代表从机无应答。这种组合方式可能与各位的编程习惯有出入,一般认为返回1表示操作成功,而返回0表示操作失败。这种方式的问题便是无法有效的表达错误原因,因为“0”只有一个,而非“0”却有很多。

写单个字节可以划分为——从机写地址发送、寄存器地址发送、寄存器内容发送。寄存器地址的发送由MSP430自动完成,这和软件模拟的操作有所区别。请勿发送I2C从机地址,若操作AT24C02发送需要写入的存储字节的首地址即可。

在单字节和多字节写操作过程中,尤其要注意UCTXSTT标志位的变化位置。UCTXSTT标志位会在从机接收完写控制字节或读控制字节之后变化,但是在写控制字节发送之后,必须先填充TXBUF,再尝试等待STT标志位复位,此时STT标志位和TXIFG标志位会同时变化。若从机没有应答,那么NACK标志位也会发生变化。再次强调需要先填充TXBUF,在等待STT标志位复位。以下代码将导致程序一直停留在while(UCB0IFG & UCTXSTT)处,具体的原因可查看MSP430参考手册。

while( UCB0CTL1 & UCTXSTP );

UCB0CTL1 |= UCTR;                 // 写模式

UCB0CTL1 |= UCTXSTT;              // 发送启动位

// 等待UCTXSTT=0 同时变化,但是很遗憾该变化不会发送

while(UCB0IFG & UCTXSTT);

UCB0TXBUF = word_addr;            // 发送字节地址

3.写多个字节

3.1代码实现

uint8_teeprom_writepage( uint8_t word_addr , uint8_t *pword_buf , uint8_t len)

{

while( UCB0CTL1 & UCTXSTP );

UCB0CTL1 |= UCTR;                 // 写模式

UCB0CTL1 |= UCTXSTT;              // 发送启动位

UCB0TXBUF = word_addr;            // 发送字节地址

// 等待UCTXIFG=1 与UCTXSTT=0 同时变化等待一个标志位即可

while(!(UCB0IFG & UCTXIFG))

{

if( UCB0IFG & UCNACKIFG )       // 若无应答 UCNACKIFG=1

{

;

}

}

; i < len ; i++ )

{

UCB0TXBUF = *pword_buf++;       // 发送寄存器内容

while(!(UCB0IFG & UCTXIFG));    // 等待UCTXIFG=1

}

UCB0CTL1 |= UCTXSTP;

while(UCB0CTL1 & UCTXSTP);        // 等待发送完成

;

}

3.2 代码分析

多字节写函数和单字节写函数相似,不做过多的解释。

4.读单个字节

单字节读函数是4中读写函数中最为复杂的,复杂的原因在于读最后一个字节之前就需要操作UCTXSTP标志位。

4.1 代码实现

uint8_teeprom_readbyte( uint8_t word_addr , uint8_t *pword_value )

{

UCB0CTL1 |= UCTR;                 // 写模式

UCB0CTL1 |= UCTXSTT;              // 发送启动位和写控制字节

UCB0TXBUF = word_addr;            // 发送字节地址,必须要先填充TXBUF

// 等待UCTXIFG=1 与UCTXSTT=0 同时变化等待一个标志位即可

while(!(UCB0IFG & UCTXIFG))

{

if( UCB0IFG & UCNACKIFG )       // 若无应答 UCNACKIFG=1

{

;

}

}

UCB0CTL1 &= ~UCTR;                // 读模式

UCB0CTL1 |= UCTXSTT;              // 发送启动位和读控制字节

while(UCB0CTL1 & UCTXSTT);        // 等待UCTXSTT=0

// 若无应答 UCNACKIFG = 1

UCB0CTL1 |= UCTXSTP;              // 先发送停止位

while(!(UCB0IFG & UCRXIFG));      // 读取字节内容

*pword_value = UCB0RXBUF;         // 读取BUF寄存器在发送停止位之后

while( UCB0CTL1 & UCTXSTP );

;

}

4.2代码分析

这段代码给人一个错觉,MSP430先发送了停止位,然后再读取了一个字节内容。其实实际情况并不是这样的。I2C读操作时,主机读取最后一个字节内容之后,应向从机发送无应答NACK(无应答区别于应答),之后主机发送停止位。MSP430为了完成这一组合动作,要求用户提前操作UCTXSTP标志位,在读取RXBUF之后做出发送NACK和I2C停止位的“组合动作”。

while(!(UCB0IFG & UCRXIFG));

*pword_value = UCB0RXBUF;         // 读取BUF寄存器在发送停止位之后

UCB0CTL1 |= UCTXSTP;              // 发送停止位

以上代码可能导致后续的I2C操作无法进行。

5.读多个字节

5.1代码实现

uint8_t eeprom_readpage(uint8_t word_addr , uint8_t *pword_buf , uint8_t len )

{

while( UCB0CTL1 & UCTXSTP );

UCB0CTL1 |= UCTR;                 // 写模式

UCB0CTL1 |= UCTXSTT;              // 发送启动位和写控制字节

UCB0TXBUF = word_addr;            // 发送字节地址

// 等待UCTXIFG=1 与UCTXSTT=0 同时变化等待一个标志位即可

while(!(UCB0IFG & UCTXIFG))

{

if( UCB0IFG & UCNACKIFG )       // 若无应答 UCNACKIFG=1

{

;

}

}

UCB0CTL1 &= ~UCTR;                // 读模式

UCB0CTL1 |= UCTXSTT;              // 发送启动位和读控制字节

while(UCB0CTL1 & UCTXSTT);        // 等待UCTXSTT=0

// 若无应答 UCNACKIFG = 1

; i++ )

{

while(!(UCB0IFG & UCRXIFG));    // 读取字节内容,不包括最后一个字节内容

*pword_buf++ = UCB0RXBUF;

}

UCB0CTL1 |= UCTXSTP;              // 在接收最后一个字节之前发送停止位

while(!(UCB0IFG & UCRXIFG));      // 读取最后一个字节内容

*pword_buf = UCB0RXBUF;

while( UCB0CTL1 & UCTXSTP );

;

}

5.2代码分析

读单个字节和写单个字节相似。唯一需要注意的是,写操作需要先填充TXBUF,而读操作不存在这个问题。试想一下,I2C写操作时必定会向I2C从机写入一个字节内容,所以先填充TXBUF也是合情合理的事情,填充TXBUF之后MSP430会进行一连串的动作——发送I2C起始位、I2C读控制器和写入从机的第一个字节。

6 单元测试

单元测试分为两个部分。单字节写函数和单字节读函数分为一组,先使用单字节邪恶函数向某地址写入某内容,在使用单字节读函数读出某内容,如果写入的参数和读出的内容相同,则测试通过。多字节写函数和多字节度函数分为一组,测试过程相似,不同的是写入的内容从一个变为了连续8个。请注意AT24C02的页大小为8,若从页首地址开始,最大的写字节个数为8。

另外,EEPROM写操作之后需要有10ms的延时,否则将无法进行写操作和读操作。具体请查看AT24C02数据手册。

6.1 测试代码

void eeprom_config()

{

#ifDEBUF_EEPROM_I2C

uint8_t test_byte1 = 0x0B;

uint8_t test_byte2 = 0x01;

/*

step1 向地址0x00写入某个值,例如0x0B

然后读出地址0x00结果,判断该值是否为0x0B

*/

eeprom_writebyte( 0x00 , test_byte1 );

delay_ms);

eeprom_readbyte( 0x00 , &test_byte2 );

assert_param( test_byte1 == test_byte2 );

if( test_byte1 == test_byte2 )

{

printf( "Byte Read andByte Write Test Pass\r\n" );

}

/*

个字节数据

字节内容,比较写入和读出字节内容

成功的条件为写入和读取字节内容相同

*/

uint8_t test_buf1};

uint8_t test_buf2};

eeprom_writepage);

delay_ms);

eeprom_readpage);

assert_param );

))

{

printf("Page Read andPage Write Test Pass!\r\n");

}

#endif

}

6.2 测试结果

7.工程代码链接

工程代码请转至百度网盘,链接如下:

http://pan.baidu.com/share/link?shareid=341219718&uk=3707837393

MSP430F5438 I2C学习笔记——AT24C02的更多相关文章

  1. 【转】树莓派学习笔记——I2C Tools 学习笔记

    原文网址:http://blog.csdn.net/xukai871105/article/details/15029843 1.安装     I2C驱动载入和速率修改请查看博文[树莓派学习笔记——I ...

  2. 【转】 树莓派学习笔记——I2C设备载入和速率设置

    原文网址:http://blog.csdn.net/xukai871105/article/details/18234075 1.载入设备 方法1——临时载入设备 sudo modprobe -r i ...

  3. 基于51单片机IIC通信的AT24C02学习笔记

    引言 最近在学习几种串行通信协议,感觉收获很多,这篇文章是学习IIC总线协议的第一篇文章,以后还会再写一篇关于PCF8591 IIC通信的ADDA转换芯片的文章. 关于IIC总线 IIC 即Inter ...

  4. 基于I2C总线的MPU6050学习笔记

    MPU6050学习笔记 1. 简述 一直想自己做个四轴飞行器,却无从下手,终于狠下决心,拿出尘封已久的MPU6050模块,开始摸索着数据手册分析,一步一步地实现了MPU6050模块的功能,从MPU60 ...

  5. 树莓派学习笔记——I2C设备载入和速率设置

    原文:http://blog.csdn.net/xukai871105/article/details/18234075 1.载入设备 方法1——临时载入设备 sudo modprobe -r i2c ...

  6. 单片机学习(十一)I2C总线和AT24C02的使用

    一. 存储器介绍 存储器分类图 1. RAM 这类存储器中的数据都是掉电即失的,例如计算机中的内存就是DRAM,但它们数据读写速度都是要比ROM要快得多的. SRAM:本质是电路,使用电路构成的触发器 ...

  7. 基于51单片机IIC通信的PCF8591学习笔记

    引言 PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入.一个输出和一个串行I2C 总线接口.3 个地址引脚A0.A1 和A2 用于编程硬件地址,允许将最多8 个器件连接 ...

  8. AM335x(TQ335x)学习笔记——挂载Ramdisk

    上篇文章中我们已经能够通过u-boot启动内核了,但是没有能够启动成功,从内核的log中可以看出,内核启动失败的原因是没有挂载到root文件系统,本文将使用busybox制作根文件系统并打包成ramd ...

  9. STM32学习笔记之一(初窥STM32)

    怎么做好学习笔记? 答:自我感知-->学习知识-->归纳总结-->自我升华(真正属于自己的知识是,抛开书本,运用时,你还能记得的思想) 自我感知--看到知识概念,先自我感觉那应该是个 ...

随机推荐

  1. Codeforces Round #259 (Div. 1) A. Little Pony and Expected Maximum 数学公式结论找规律水题

    A. Little Pony and Expected Maximum Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.c ...

  2. LINUX mysql 源码安装

    一.下载编译安装 #cd /usr/local/src/ #wget http://mysql.byungsoo.net/Downloads/MySQL-5.1/mysql-5.1.38.tar.gz ...

  3. sunstudio 12.3 on solaris 10

    #include <stdio.h> #include <stdlib.h> #define RPT 10000000 int main() { int i=1; for( i ...

  4. 创建新的Cocos2dx 3.0项目并解决一些编译问题

    转载请注明出处:http://blog.csdn.net/cywn_d/article/details/25775019 假设是原来使用cocos2dx 2.x要升级到3.0的项目,可能须要替换coc ...

  5. hadoop函数说明图

  6. 手机应用:非功能需求 Check List

    服务状态防止并发 网络保持:无线网络,GPRS 网络连接:https,手机助手代理 电量 屏幕保持防止休眠 下载重试机制 定时检查XML 限速下载,线程休眠 下载出错反馈机制 消息广播 状态栏通知 进 ...

  7. python测试开发django-9.使用navicat连接mysql

    前言 navicat 是一个连接数据库的可视化工具,可以连接mysql和oracle做一些简单增删改查,对于初学者来说非常方便的 navicat安装 navicat版本比较多,分享一个我经常用的版本 ...

  8. Android之与当前连接的wifi进行文件夹的浏览与传输

    先上传jar文件:ConnectWifi.jar.zip 上传源文件:org.zip 使用实例及相应的注释: import java.io.File; import java.net.InetAddr ...

  9. openssh-server

    安装 apt-get install openssh-server 配置 sudo gedit /etc/ssh/sshd_config PermitRootLogin without-passwor ...

  10. cout的输出格式初探

    在C++中,cout代表的是标准输出设备,即显示器,相对于C语言中所使用的printf函数,cout显得更为灵活.下面以30.300和1024三个数为例子,简单说明cout输出时所选格式的输出.cou ...