0. 前言
对于大多数单片机来说,I2C成了一个老大难问题。从51时代开始,软件模拟I2C成了主流,甚至到ARMCortex M3大行其道的今天,软件模拟I2C依然是使用最广的方法。虽然软件模拟可以解决所有的问题,但是总感觉没有充分发挥MCU内部的硬件资源。查阅了所有关于MSP430F5系列的图书,没有关于硬件I2C的应用代码,自己通过调试摸索,把经验总结之后和大家分享,希望大家喜欢。同时,I2C的使用可以分为等待法和中断法,从理解的角度来说等待法思路清晰易于上手,从功耗的角度出发,中断法可以灵活的进入低功耗模式,但是不易理解。本文先从等待法入手。
MSP430F5系列的硬件I2C使用大致会有以下问题:
n I2C地址设定。一般情况下I2C的7位地址被写成了8位长度,最低位无效。例如AT24C02的I2C地址为0xA0,其实真正的7位地址为0x50。而MSP430正是需要填入这7位地址0x50。
n I2C停止位发送。在I2C读操作过程中,读取最后一个字节之后MCU应向从机发送无应答,MSP430F5系列的MCU发送无应答的操作将自动完成,这就以为在读取最后一个字节内容时,应先操作停止位相关寄存器。
n I2C起始位发送。如果仔细分析MSP430F5参考手册,将会发现读操作和写操作发送I2C起始位时略有不同。写操作时需要先向TXBUF中写入数据,之后才可以等待TXSTT标志位变为0,而读操作和写操作稍有不同。
1. 初始化设置
1.1代码实现
void ucb0_config(void)
{
P3SEL &= ~BIT2; // P3.2@UCB0SCL
P3DIR |= BIT2;
P3OUT |= BIT2;
// 输出9个时钟 以恢复I2C总线状态
for( uint8_t i = 0 ; i < 9 ; i++ )
{
P3OUT |= BIT2;
__delay_cycles(8000);
P3OUT &= ~BIT2;
__delay_cycles(8000);
}
P3SEL |= (BIT1 + BIT2); // P3.1@UCB0SDAP3.2@UCB0SCL
UCB0CTL1 |= UCSWRST;
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC ; // I2C主机模式
UCB0CTL1 |= UCSSEL_2; // 选择SMCLK
UCB0BR0 = 40;
UCB0BR1 = 0;
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地址寄存器应写入7位地址,参照上面的例子应写入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
{
return 1;
}
}
UCB0TXBUF = word_value; // 发送字节内容
while(!(UCB0IFG & UCTXIFG)); // 等待UCTXIFG=1
UCB0CTL1 |= UCTXSTP;
while(UCB0CTL1 & UCTXSTP); // 等待发送完成
return 0;
}
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
{
return 1;
}
}
for( uint8_t i = 0 ; i < len ; i++ )
{
UCB0TXBUF = *pword_buf++; // 发送寄存器内容
while(!(UCB0IFG & UCTXIFG)); // 等待UCTXIFG=1
}
UCB0CTL1 |= UCTXSTP;
while(UCB0CTL1 & UCTXSTP); // 等待发送完成
return 0;
}
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
{
return 1;
}
}
UCB0CTL1 &= ~UCTR; // 读模式
UCB0CTL1 |= UCTXSTT; // 发送启动位和读控制字节
while(UCB0CTL1 & UCTXSTT); // 等待UCTXSTT=0
// 若无应答 UCNACKIFG = 1
UCB0CTL1 |= UCTXSTP; // 先发送停止位
while(!(UCB0IFG & UCRXIFG)); // 读取字节内容
*pword_value = UCB0RXBUF; // 读取BUF寄存器在发送停止位之后
while( UCB0CTL1 & UCTXSTP );
return 0;
}
4.2 代码分析
这段代码给人一个错觉,MSP430先发送了停止位,然后再读取了一个字节内容。其实实际情况并不是这样的。I2C读操作时,主机读取最后一个字节内容之后,应向从机发送无应答NACK(无应答区别于应答),之后主机发送停止位。MSP430为了完成这一组合动作,要求用户提前操作UCTXSTP标志位,在读取RXBUF之后做出发送NACK和I2C停止位的“组合动作”。
while(!(UCB0IFG & UCRXIFG));
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
{
return 1;
}
}
UCB0CTL1 &= ~UCTR; // 读模式
上一篇:IAR for MSP430安装教程
下一篇:基于MSP430F5529单片机的DAC8552
推荐阅读最新更新时间:2024-11-12 15:21
设计资源 培训 开发板 精华推荐
- ADP7118RD-EVALZ、LFCSP 评估板,用于评估 ADP7118 20V、200mA 低噪声、CMOS LDO
- LTC2992IDE-1 双极性电源功率监视器的典型应用(1.5kHz I2C 接口)
- NCV2002SN1T1G高端电流检测典型应用电路
- 2相单极步进电机驱动IC —— TB67S142HG
- 用于检查初始化的测量设置应用电路
- ADP2138CB-0.8EVALZ,ADP2138 评估板,800 mA,输出电压 0.8 V,3 MHz 降压稳压器
- #第七届立创电赛#USB电流测量
- 使用 RP40-11015SFR DC/DC 转换器并根据 EN55022 A 类(双输出)进行 EMC 滤波的典型应用
- NCP1587A 低压同步降压控制器的典型应用
- 用于有线网络的 400W、400V 交流转直流单输出电源