MSP430F5438 I2C学习笔记——AT24C02

发布者:MysticGlow最新更新时间:2017-02-20 来源: eefocus关键字:MSP430F5438  I2C  AT24C02 手机看文章 扫描二维码
随时随地手机看文章

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,而读操作和写操作稍有不同。

【AT24C02操作时序图】



1.初始化设置

1.1代码实现


  1. void ucb0_config(void)  

  2. {  

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

  4.   P3DIR |= BIT2;  

  5.   P3OUT |= BIT2;  

  6.   // 输出9个时钟以恢复I2C总线状态  

  7.   for( uint8_t i= 0; i <9 ; i++)  

  8.   {  

  9.     P3OUT |= BIT2;  

  10.     __delay_cycles(8000);  

  11.     P3OUT &= ~BIT2;  

  12.     __delay_cycles(8000);  

  13.   }  

  14.    

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

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

  17.    

  18.   UCB0CTL1 |= UCSWRST;  

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

  20.   UCB0CTL1 |= UCSSEL_2;                  // 选择SMCLK  

  21.   UCB0BR0 = 40;  

  22.   UCB0BR1 = 0;  

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

  24.   UCB0I2CSA = EEPROM_ADDRESS;           // EEPROM地址  

  25.   UCB0CTL1 &= ~UCSWRST;  

  26. }  


1.2代码分析

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

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

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


2.写单个字节

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


2.1 代码实现


  1. uint8_teeprom_writebyte( uint8_t word_addr, uint8_tword_value )  

  2. {  

  3.   while( UCB0CTL1& UCTXSTP );  

  4.   UCB0CTL1 |= UCTR;                // 写模式  

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

  6.    

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

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

  9.   while(!(UCB0IFG& UCTXIFG))  

  10.   {  

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

  12.     {  

  13.       return 1;  

  14.     }  

  15.   }    

  16.    

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

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

  19.    

  20.   UCB0CTL1 |= UCTXSTP;  

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

  22.    

  23.   return 0;  

  24. }  



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参考手册。


[cpp] view plain copy 在CODE上查看代码片派生到我的代码片

  1. while( UCB0CTL1& UCTXSTP );  

  2. UCB0CTL1 |= UCTR;                // 写模式  

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

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

  5. while(UCB0IFG& UCTXSTT);  

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



3.写多个字节

3.1代码实现


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

  2. {  

  3.   while( UCB0CTL1& UCTXSTP );  

  4.   UCB0CTL1 |= UCTR;                // 写模式  

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

  6.    

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

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

  9.   while(!(UCB0IFG& UCTXIFG))  

  10.   {  

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

  12.     {  

  13.       return 1;  

  14.     }  

  15.   }    

  16.    

  17.   for( uint8_t i= 0; i < len; i++)  

  18.   {  

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

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

  21.   }  

  22.    

  23.   UCB0CTL1 |= UCTXSTP;  

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

  25.    

  26.   return 0;  

  27. }  



3.2 代码分析

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


4.读单个字节

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

4.1 代码实现


  1. uint8_teeprom_readbyte( uint8_t word_addr, uint8_t *pword_value)  

  2. {  

  3.   UCB0CTL1 |= UCTR;                // 写模式  

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

  5.    

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

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

  8.   while(!(UCB0IFG& UCTXIFG))  

  9.   {  

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

  11.     {  

  12.       return 1;  

  13.     }  

  14.   }                         

  15.    

  16.   UCB0CTL1 &= ~UCTR;                //读模式  

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

  18.    

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

  20.   // 若无应答 UCNACKIFG = 1  

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

  22.    

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

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

  25.    

  26.   while( UCB0CTL1& UCTXSTP );  

  27.    

  28.   return 0;  

  29. }  


4.2代码分析

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



  1. while(!(UCB0IFG& UCRXIFG));  

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

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



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


5.读多个字节

5.1代码实现


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

  2. {  

  3.   while( UCB0CTL1& UCTXSTP );  

  4.   UCB0CTL1 |= UCTR;                // 写模式  

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

  6.    

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

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

  9.   while(!(UCB0IFG& UCTXIFG))  

  10.   {  

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

  12.     {  

  13.       return 1;  

  14.     }  

  15.   }    

  16.    

  17.   UCB0CTL1 &= ~UCTR;               // 读模式  

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

  19.    

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

  21.   // 若无应答 UCNACKIFG = 1  

  22.    

  23.   for( uint8_t i= 0; i< len -1 ; i++)  

  24.   {  

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

  26.     *pword_buf++= UCB0RXBUF;  

  27.   }  

  28.    

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

  30.    

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

  32.   *pword_buf = UCB0RXBUF;  

  33.    

  34.   while( UCB0CTL1& UCTXSTP );  

  35.    

  36.   return 0;  

  37. }  



5.2代码分析

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


6 单元测试

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

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

6.1 测试代码



  1. void eeprom_config()  

  2. {  

  3. #ifDEBUF_EEPROM_I2C  

  4.    

  5.   uint8_t test_byte1 =0x0B;  

  6.   uint8_t test_byte2 =0x01;  

  7.    

  8.   /* 

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

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

  11.   */  

  12.   eeprom_writebyte(0x00 , test_byte1);   

  13.   delay_ms(10);  

  14.   eeprom_readbyte(0x00 ,&test_byte2 );  

  15.   assert_param( test_byte1== test_byte2 );  

  16.    

  17.   if( test_byte1== test_byte2 )  

  18.   {  

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

  20.   }  

  21.    

  22.   /* 

  23.     step2 以地址0x08作为起始地址,连续写入8个字节数据 

  24.           再连续从该起始地址读取8字节内容,比较写入和读出字节内容 

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

  26.   */  

  27.   uint8_t test_buf1[8]= {1,2,3,4,5,6,7,8};  

  28.   uint8_t test_buf2[8]= {0,0,0,0,0,0,0,0};  

  29.    

  30.   eeprom_writepage(0x08 , test_buf1, 8);  

  31.   delay_ms(10);  

  32.   eeprom_readpage(0x08 , test_buf2, 8);  

  33.   assert_param( memcmp( test_buf1, test_buf2 ,8) == 0 );  

  34.    

  35.   if(!memcmp( test_buf1, test_buf2 ,8))  

  36.   {  

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

  38.   }  

  39.    

  40. #endif  

  41. }  


6.2 测试结果


关键字:MSP430F5438  I2C  AT24C02 引用地址:MSP430F5438 I2C学习笔记——AT24C02

上一篇:如何在FreeRTOS下实现低功耗——MSP430F5438平台
下一篇:CC2530 RF部分使用 ——实现点对点收发

推荐阅读最新更新时间:2024-03-16 15:34

STM32-(20):I2C通信(实验:读写EEPROM)
硬件电路连接 底板上的 I2C 接口 A0、A1是用来确定器件的地址的。 排针上的引脚图: 核心板上的引脚图: 通过I2C总线实现对EEPROM的读写操作的准备工作: 1、掌握芯片(目标对象)特性,才能对其正确的读和写。 2、掌握 I2C 通信,读写过程需要用到。 3、Cortex的一些操作,编程方法。 实验内容:通过I2C总线实现对EEPROM的读写操作 main.c #include stm32f10x_lib.h #include IIC.h /*------------函数的声明---------------*/ void Delay_MS(u16 dly); void RCC_Configurati
[单片机]
STM32-(20):<font color='red'>I2C</font>通信(实验:读写EEPROM)
I2C串行总线的组成据工作原理
1、常用的串行扩展总线有:I2C总线、单总线、SPI总线 2、I2C总线只有两根双向信号线。一种是数据线SDA,另一种是时钟线SCL。 3、I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的 任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关 系。 4、I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定, 只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。 5、SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平 期间,SDA线由低电平向高电平的变化表示终止信号。 6、起始和终止信
[单片机]
I2C之知(六)--s3c2440用I2C接口访问EEPROM
        在前面阅读理解了I2C的官方协议文档后,就拿s3c2440和EEPROM来验证一下.         本来是想用s3c2440的SDA和SCL管脚复用为GPIO来模拟的,但在没有示波器的情况下搞了一周,怎么都出不来,最后还是放弃了.甚至参考了linux下i2c-algo-bit.c和i2c-gpio.c,依然没调出来.如果有示波器,可能很快就能找到原因,现在完全不知道问题出在哪里.其实想用GPIO模拟I2C的目的很简单,以一种简单而又深刻的方式来理解I2C.         既然这条路暂时没法走,退而求其次,用s3c2440的I2C接口来访问EEPROM,只要按照datasheet的来做,基本上不用考虑时序咯.
[嵌入式]
设计一个属于您自己的简易 I2C 隔离器
通常产品设计时间非常紧张,用于新产品设计的资金也并不宽余。但不管怎样,我们都必须要在不增加成本的前提下设计出能够运行于恶劣环境下的稳健系统。一般而言,这会要求使用 电 流隔离,用于保护敏感控制电子组件免受外部突入和瞬态浪涌电流的损害。 如果您的设计涉及许多工业接口,那么当您在各大半导体厂商的官方网站上看到琳琅满目的RS-485、RS-232、CAN和I2C信号隔离器时,您会发现自己像一个进到糖果店里的小孩一样兴奋不已。但是,当您想要采购经理批准购买这些产品时,他会立马给您泼上一盆冷水: 不能利用一些已有的标准组件吗?不管用什么方法,把它们都利用起来? 今后碰到这种情况,您可以热情洋溢的回答 没问题 了,因为本文将为您介绍一小部
[单片机]
设计一个属于您自己的简易 <font color='red'>I2C</font> 隔离器
MSP430F5438 定时器总结
1.MSP430F5438有三个定时器 TA0 TA1和TB,定时器的功能略有区别。 2.对于定时器TA1而言,有两个中断向量地址,其中比较匹配通道0具有单独的中断向量 3.MSP430中断向量的名称和TA0 TA1很难对应起来,需要通过中断向量地址来确认。 #define TIMER1_A1_VECTOR (48 * 2u) /* 0xFFE0 Timer1_A3 CC1-2, TA1 */ #define TIMER1_A0_VECTOR (49 * 2u) /* 0xFFE2 Timer1_A3 CC0 */ #define TIMER0_A1_VECTOR (53 * 2u) /* 0xFFEA T
[单片机]
<font color='red'>MSP430F5438</font> 定时器总结
51单片机OLED12864 I2C接口使用教程
现在能买到的OLED12864显示屏大多为SPI和I2C接口的,I2C通信协议只需要两条总线就可以进行通信,下面介绍一下如何用51单片机使用I2C接口的OLED12864。 首先介绍一下I2C通信协议,I2C(Inter-Integrated Circuit)字面上的意思是集成电路之间,它其实是I2CBus简称,所以中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。I2C的正确读法为“I平方C”( I-squared-C )。 I2C只使用两条双向漏极开路(Open Drain)(串行数据(SDA)及串行时钟频率(S
[单片机]
51单片机OLED12864 <font color='red'>I2C</font>接口使用教程
STC89C52+AT24C02实现设备开机次数记录
一、项目介绍 在一些设备的使用过程中,需要对设备的使用次数进行统计和记录。这可以用于评估设备的实际使用寿命、确定维护周期、预测故障风险等方面,对于提高设备的稳定性和可靠性具有重要意义。 当前项目采用STC89C52作为主控芯片,AT24C02作为存储芯片,实现了设备的开机次数记录功能。每次设备上电启动时,程序会从AT24C02中读取之前的记录值并加1,然后再将新的记录值写入AT24C02中,从而完成一次开机次数的记录。通过这种方式,可以实时、准确地记录设备的使用次数,并且不受断电影响,数据可靠性高 二、AT24C02介绍 AT24C02是一款由Atmel公司生产的串行EEPROM存储器芯片,可以存储2K(2048bit)
[单片机]
STC89C52+AT24C02实现设备开机次数记录
基于I2C总线控制的音频处理电路设计
0 引 言 当前汽车音响与高保真的立体声音响系统中都包含了微处理器电路单元,这为实现音频处理提供了控制接口,可以通过控制接口实现许多需要的功能控制。作为音响系统主体的音频处理电路性能直接决定了整个音响系统质量,设计高性能的音频处理电路是该文的核心部分。 该设计的高性能音频处理电路基于I2C总线控制协议,包含输入多通道选择、音量控制、高低音音效处理、输出通道平衡度调整等功能,适合应用于高质量汽车音响、高保真收音机、彩电、家庭组合音响系统。 1 电路模块的设计 高保真音响系统的系统结构图如图1所示.其中音频处理电路的设计和功率放大器的设计往往是利用不同的芯片来完成的。 根据高保真立体声高级音响系统对音频处理电
[工业控制]
基于<font color='red'>I2C</font>总线控制的音频处理电路设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved