MSP430非模拟IIC总线控制程序
2015-04-13 来源:eechina
对于MSP430的学习经历一个从痛苦到对430很有感情的转变.当然开始学习的时候那是相当恼火.网上也没有什么很多的相关资料.就算有资料也是给不全.参考与学习都不很方便.经过多方面的努力和找书再到对程序的仔细读,感到非模拟的总线带来的方便还是很多的. 下面就是程序和流程图:
IIC.h
void Init_IIC(void);
void EEPROM_ByteWrite(unsigned char nAddr,unsigned char nVal);
unsigned char EEPROM_RandomRead(unsigned char nAddr);
unsigned char EEPROM_CurrentAddressRead(void);
void EEPROM_AckPolling(void);
void Init_CLK(void);
void Init_IIC_Port(void);
Main.C
/*******************************************
IIC for AT24c16 OR AT24CXXX 系列
只要控制好IICRM IICSTP IICSTT 其硬件会自动完成
SCL SDA的一系列时序 只要注意各个发送与接收的控制标志位.
******************************************/
#include
#include 'IIC.h'
volatile unsigned char Data[6];
void main(void)
{
//volatile unsigned char Data[6];
//停止看门狗
WDTCTL = WDTPW+WDTHOLD;
//初始化端口
Init_IIC_Port();
//初始化时钟
Init_CLK();
//I2C初始化
Init_IIC(); //置传输方式及控制方式
//打开中断
_EINT();
//写入数据
EEPROM_ByteWrite(0x0000,0x12);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0001,0x34);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0002,0x56);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0003,0x78);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0004,0x9A);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0005,0xBC);
//等待写操作完成
EEPROM_AckPolling();
//读出数据,随机读
Data[0] = EEPROM_RandomRead(0x0000); //地址自动加1
//读出数据,当前地址读
Data[1] = EEPROM_CurrentAddressRead();
//读出数据,当前地址读
Data[2] = EEPROM_CurrentAddressRead();
//读出数据,当前地址读
Data[3] = EEPROM_CurrentAddressRead();
//读出数据,当前地址读
Data[4] = EEPROM_CurrentAddressRead();
//读出数据,当前地址读
Data[5] = EEPROM_CurrentAddressRead();
}
IIC.C
#include
#include 'IIC.h'
#define SLAVEADDR 0x50;
int tx_count;
int rx_count;
unsigned char I2CBuffer[3];
void Init_IIC(void)
{
//将P3.1和P3.3设置为I2C管脚
P3SEL = 0x0A;
//设置P3.1和P3.3管脚的方向
P3DIR &= ~0x0A;
//选择为I2C模式
U0CTL |= I2C + SYNC;
//禁止I2C模块
U0CTL &= ~I2CEN;
//设置I2C为7位地址模式,不使用DMA,
//字节模式,时钟源为SMCLK,
//设置成传输模式
I2CTCTL = I2CTRX + I2CSSEL_2;
//定义从器件地址
I2CSA = SLAVEADDR;
//设置本身的地址
I2COA = 0x01A5;
//I2C时钟为SMCLK / 160
I2CPSC = 159;
//SCL 高电平为:5 *I2C 时钟
I2CSCLH = 0x03;
//SCL 低电平为:5 *I2C 时钟
I2CSCLL = 0x03;
//I2C 模块有效
U0CTL |= I2CEN;
tx_count = 0;
rx_count = 0;
}
void I2CWriteInit(void) //对于AT24CXXX的写操作是置成主模式并置位中断使能.
{
//主(Master)模式
U0CTL |= MST;
//传输模式,R/W 为:0
I2CTCTL |= I2CTRX;
//清除中断标志
I2CIFG &= ~TXRDYIFG;
//发送中断使能
I2CIE = TXRDYIE;
}
void I2CReadInit(void)
{
//接收模式,R/W 为:1
I2CTCTL &= ~I2CTRX;
//接收中断使能
I2CIE = RXRDYIE;
}
void EEPROM_ByteWrite(unsigned char nAddr, unsigned char nVal)
{
//等待I2C模块完成所有操作 //在选定的地址写入数据.
while (I2CDCTL&I2CBUSY) ;
//设置地址数据
I2CBuffer[1] = nAddr;
//设置数据
I2CBuffer[0] = nVal;
//设置缓冲区指针
tx_count = 1;
//写数据初始化
I2CWriteInit(); //设置为主模式
//发送数据的长度
//1个控制字节,2个数据字节
I2CNDAT = 2;
//开始和停止条件产生
//开始I2C通信
I2CTCTL |= I2CSTT+I2CSTP;
return;
}
unsigned char EEPROM_CurrentAddressRead(void)
{
//等待I2C模块完成所有操作
while (I2CDCTL&I2CBUSY);
//读操作的初始化
I2CReadInit();
//主(Master)模式
U0CTL |= MST;
//接收1个字节的数据
I2CNDAT = 1;
//清除中断标志
I2CIFG &= ~ARDYIFG;
//开始接收,产生重新起始和停止条件
I2CTCTL |= I2CSTT + I2CSTP;
//等待传输完成
while ((~I2CIFG)&ARDYIFG) ;
//返回数据
return I2CBuffer[0];
}
unsigned char EEPROM_RandomRead(unsigned char nAddr)
{
//等待I2C模块完成所有操作
while (I2CDCTL&I2CBUSY);
//设置地址
I2CBuffer[0] = nAddr;
//设置缓冲区指针
tx_count = 0;
//写操作初始化
I2CWriteInit();
//传输数据长度
//1个控制字节和一个地址数据
I2CNDAT = 1;
//清除中断标志
I2CIFG &= ~ARDYIFG;
//起始条件产生
I2CTCTL |= I2CSTT;
//等待传输完成
while ((~I2CIFG)&ARDYIFG);
//读操作初始化
I2CReadInit();
//接收一个字节的数据
I2CNDAT = 1;
//清除中断标志
I2CIFG &= ~ARDYIFG;
//开始接收,产生重新起始和停止条件
I2CTCTL |= I2CSTT + I2CSTP;
//等待传输完成
while ((~I2CIFG)&ARDYIFG);
//返回数据
return I2CBuffer[0];
}
void EEPROM_AckPolling(void)
{
unsigned int count;
//等待I2C模块完成所有操作
while (I2CDCTL&I2CBUSY);
count=0;
//清除I2CEN位
U0CTL &= ~I2CEN;
I2CTCTL |= I2CRM;
//使能I2C模块
U0CTL |= I2CEN;
//设置NACKIFG标志
I2CIFG = NACKIFG;
while (NACKIFG & I2CIFG)
{
//清除中断标志
I2CIFG=0x00;
//主(Master)模式
U0CTL |= MST;
//设置传输模式
I2CTCTL |= I2CTRX;
//产生起始条件
I2CTCTL |= I2CSTT;
//等待I2CSTT被清除
while (I2CTCTL & I2CSTT) ;
//产生停止条件
I2CTCTL |= I2CSTP;
//等待停止条件复位
while (I2CDCTL & I2CBUSY) ;
count = count + 1;
}
//清除I2CEN位
U0CTL &= ~I2CEN;
I2CTCTL &= ~I2CRM;
//使能I2C
U0CTL |= I2CEN;
return;
}
#if __VER__ < 200
interrupt [USART0TX_VECTOR] void ISR_I2C(void)
#else
#pragma vector=USART0TX_VECTOR
__interrupt void ISR_I2C(void)
#endif //上面的程序其实只要编写 :
//#pragma vector=USART0TX_VECTOR __interrupt void ISR_I2C(void)就行.
{
switch (I2CIV)
{
case I2CIV_AL:
{
//仲裁中断
break;
}
case I2CIV_NACK:
{
//NACK中断
break;
}
case I2CIV_OA:
{
//自己地址中断
break;
}
case I2CIV_ARDY:
{
//访问准备好中断
break;
}
case I2CIV_RXRDY:
{
//接收准备好中断
I2CBuffer[0]=I2CDRB;
break;
}
case I2CIV_TXRDY:
{
//发送准备好中断
I2CDRB = I2CBuffer[tx_count];
tx_count = tx_count - 1;
if (tx_count < 0)
{
//禁止发送中断
I2CIE &= ~TXRDYIE;
}
break;
}
case I2CIV_GC:
{
//一般调用中断
break;
}
case I2CIV_STT:
{
//起始条件中断
break;
}
}
}
void Init_IIC_Port(void)
{
//初始化端口寄存器 与IIC口无关的PX口关闭以便于对编写系统板的综合程序.
//P1DIR = 0xFF;
//P2DIR = 0xFF;
P3DIR = 0xF5;
//P4DIR = 0xFF;
P5DIR = 0x7F;
//P6DIR = 0xFF;
//P4OUT = 0X11;
//P5OUT &= 0XF0;
P3SEL|=BIT1+BIT3; //在这里如果设置成
}
void Init_CLK(void)
{
unsigned int i;
//将寄存器的内容清零
//XT2震荡器开启
//LFTX1工作在低频模式
//ACLK的分频因子为1
BCSCTL1 = 0X00;
do
{
// 清除OSCFault标志
IFG1 &= ~OFIFG;
for (i = 0x20; i > 0; i--);
}
while ((IFG1 & OFIFG) == OFIFG); // 如果OSCFault =1
//open XT2, LFTX2 选择低频率
BCSCTL1 &= ~(XT2OFF + XTS); //BCSCTL1=0X00 功能一样
//DCO Rsel=7(Freq=3200k/25摄氏度)
BCSCTL1 |= RSEL0 + RSEL1 + RSEL2;
BCSCTL1 |= 0x07;
//MCLK的时钟源为TX2CLK,分频因子为1
BCSCTL2 += SELM1;
//SMCLK的时钟源为TX2CLK,分频因子为1
BCSCTL2 += SELS;
}
//对于系统时钟的选择关系到整个程序运行稳定性.
看到很多卖开发板的人将IIC硬件写上去后再去搞个模拟的IIC总线程序. 感觉到有点说不出的感觉. 其实430的IIC不是专用来外扩展FLASH的,而是用来和一些特殊的电路连接,实现功能. 对于MSP430147~149 15X 16X 的芯片内部有48~60K的Flash了还有必要来个模拟的IIC总线时序么.装个UCOS都可以了.开发板要做的事情就是如何做好非模拟IIC程序的设计.更不是为了和C1搞比拼抢占市场.
上面的程序是经过MSP430F1611的测试.程序的大部分来自上,曾想自己从新开发定义一个,但想到网络上没有这个程序的完整版.我就修改了其中的几个地方.一方面便于自己查看并复习也适于网络上的朋友来讨论交流.
进入单片机查看更多内容>>
IIC.h
void Init_IIC(void);
void EEPROM_ByteWrite(unsigned char nAddr,unsigned char nVal);
unsigned char EEPROM_RandomRead(unsigned char nAddr);
unsigned char EEPROM_CurrentAddressRead(void);
void EEPROM_AckPolling(void);
void Init_CLK(void);
void Init_IIC_Port(void);
Main.C
/*******************************************
IIC for AT24c16 OR AT24CXXX 系列
只要控制好IICRM IICSTP IICSTT 其硬件会自动完成
SCL SDA的一系列时序 只要注意各个发送与接收的控制标志位.
******************************************/
#include
#include 'IIC.h'
volatile unsigned char Data[6];
void main(void)
{
//volatile unsigned char Data[6];
//停止看门狗
WDTCTL = WDTPW+WDTHOLD;
//初始化端口
Init_IIC_Port();
//初始化时钟
Init_CLK();
//I2C初始化
Init_IIC(); //置传输方式及控制方式
//打开中断
_EINT();
//写入数据
EEPROM_ByteWrite(0x0000,0x12);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0001,0x34);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0002,0x56);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0003,0x78);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0004,0x9A);
//等待写操作完成
EEPROM_AckPolling();
//写入数据
EEPROM_ByteWrite(0x0005,0xBC);
//等待写操作完成
EEPROM_AckPolling();
//读出数据,随机读
Data[0] = EEPROM_RandomRead(0x0000); //地址自动加1
//读出数据,当前地址读
Data[1] = EEPROM_CurrentAddressRead();
//读出数据,当前地址读
Data[2] = EEPROM_CurrentAddressRead();
//读出数据,当前地址读
Data[3] = EEPROM_CurrentAddressRead();
//读出数据,当前地址读
Data[4] = EEPROM_CurrentAddressRead();
//读出数据,当前地址读
Data[5] = EEPROM_CurrentAddressRead();
}
IIC.C
#include
#include 'IIC.h'
#define SLAVEADDR 0x50;
int tx_count;
int rx_count;
unsigned char I2CBuffer[3];
void Init_IIC(void)
{
//将P3.1和P3.3设置为I2C管脚
P3SEL = 0x0A;
//设置P3.1和P3.3管脚的方向
P3DIR &= ~0x0A;
//选择为I2C模式
U0CTL |= I2C + SYNC;
//禁止I2C模块
U0CTL &= ~I2CEN;
//设置I2C为7位地址模式,不使用DMA,
//字节模式,时钟源为SMCLK,
//设置成传输模式
I2CTCTL = I2CTRX + I2CSSEL_2;
//定义从器件地址
I2CSA = SLAVEADDR;
//设置本身的地址
I2COA = 0x01A5;
//I2C时钟为SMCLK / 160
I2CPSC = 159;
//SCL 高电平为:5 *I2C 时钟
I2CSCLH = 0x03;
//SCL 低电平为:5 *I2C 时钟
I2CSCLL = 0x03;
//I2C 模块有效
U0CTL |= I2CEN;
tx_count = 0;
rx_count = 0;
}
void I2CWriteInit(void) //对于AT24CXXX的写操作是置成主模式并置位中断使能.
{
//主(Master)模式
U0CTL |= MST;
//传输模式,R/W 为:0
I2CTCTL |= I2CTRX;
//清除中断标志
I2CIFG &= ~TXRDYIFG;
//发送中断使能
I2CIE = TXRDYIE;
}
void I2CReadInit(void)
{
//接收模式,R/W 为:1
I2CTCTL &= ~I2CTRX;
//接收中断使能
I2CIE = RXRDYIE;
}
void EEPROM_ByteWrite(unsigned char nAddr, unsigned char nVal)
{
//等待I2C模块完成所有操作 //在选定的地址写入数据.
while (I2CDCTL&I2CBUSY) ;
//设置地址数据
I2CBuffer[1] = nAddr;
//设置数据
I2CBuffer[0] = nVal;
//设置缓冲区指针
tx_count = 1;
//写数据初始化
I2CWriteInit(); //设置为主模式
//发送数据的长度
//1个控制字节,2个数据字节
I2CNDAT = 2;
//开始和停止条件产生
//开始I2C通信
I2CTCTL |= I2CSTT+I2CSTP;
return;
}
unsigned char EEPROM_CurrentAddressRead(void)
{
//等待I2C模块完成所有操作
while (I2CDCTL&I2CBUSY);
//读操作的初始化
I2CReadInit();
//主(Master)模式
U0CTL |= MST;
//接收1个字节的数据
I2CNDAT = 1;
//清除中断标志
I2CIFG &= ~ARDYIFG;
//开始接收,产生重新起始和停止条件
I2CTCTL |= I2CSTT + I2CSTP;
//等待传输完成
while ((~I2CIFG)&ARDYIFG) ;
//返回数据
return I2CBuffer[0];
}
unsigned char EEPROM_RandomRead(unsigned char nAddr)
{
//等待I2C模块完成所有操作
while (I2CDCTL&I2CBUSY);
//设置地址
I2CBuffer[0] = nAddr;
//设置缓冲区指针
tx_count = 0;
//写操作初始化
I2CWriteInit();
//传输数据长度
//1个控制字节和一个地址数据
I2CNDAT = 1;
//清除中断标志
I2CIFG &= ~ARDYIFG;
//起始条件产生
I2CTCTL |= I2CSTT;
//等待传输完成
while ((~I2CIFG)&ARDYIFG);
//读操作初始化
I2CReadInit();
//接收一个字节的数据
I2CNDAT = 1;
//清除中断标志
I2CIFG &= ~ARDYIFG;
//开始接收,产生重新起始和停止条件
I2CTCTL |= I2CSTT + I2CSTP;
//等待传输完成
while ((~I2CIFG)&ARDYIFG);
//返回数据
return I2CBuffer[0];
}
void EEPROM_AckPolling(void)
{
unsigned int count;
//等待I2C模块完成所有操作
while (I2CDCTL&I2CBUSY);
count=0;
//清除I2CEN位
U0CTL &= ~I2CEN;
I2CTCTL |= I2CRM;
//使能I2C模块
U0CTL |= I2CEN;
//设置NACKIFG标志
I2CIFG = NACKIFG;
while (NACKIFG & I2CIFG)
{
//清除中断标志
I2CIFG=0x00;
//主(Master)模式
U0CTL |= MST;
//设置传输模式
I2CTCTL |= I2CTRX;
//产生起始条件
I2CTCTL |= I2CSTT;
//等待I2CSTT被清除
while (I2CTCTL & I2CSTT) ;
//产生停止条件
I2CTCTL |= I2CSTP;
//等待停止条件复位
while (I2CDCTL & I2CBUSY) ;
count = count + 1;
}
//清除I2CEN位
U0CTL &= ~I2CEN;
I2CTCTL &= ~I2CRM;
//使能I2C
U0CTL |= I2CEN;
return;
}
#if __VER__ < 200
interrupt [USART0TX_VECTOR] void ISR_I2C(void)
#else
#pragma vector=USART0TX_VECTOR
__interrupt void ISR_I2C(void)
#endif //上面的程序其实只要编写 :
//#pragma vector=USART0TX_VECTOR __interrupt void ISR_I2C(void)就行.
{
switch (I2CIV)
{
case I2CIV_AL:
{
//仲裁中断
break;
}
case I2CIV_NACK:
{
//NACK中断
break;
}
case I2CIV_OA:
{
//自己地址中断
break;
}
case I2CIV_ARDY:
{
//访问准备好中断
break;
}
case I2CIV_RXRDY:
{
//接收准备好中断
I2CBuffer[0]=I2CDRB;
break;
}
case I2CIV_TXRDY:
{
//发送准备好中断
I2CDRB = I2CBuffer[tx_count];
tx_count = tx_count - 1;
if (tx_count < 0)
{
//禁止发送中断
I2CIE &= ~TXRDYIE;
}
break;
}
case I2CIV_GC:
{
//一般调用中断
break;
}
case I2CIV_STT:
{
//起始条件中断
break;
}
}
}
void Init_IIC_Port(void)
{
//初始化端口寄存器 与IIC口无关的PX口关闭以便于对编写系统板的综合程序.
//P1DIR = 0xFF;
//P2DIR = 0xFF;
P3DIR = 0xF5;
//P4DIR = 0xFF;
P5DIR = 0x7F;
//P6DIR = 0xFF;
//P4OUT = 0X11;
//P5OUT &= 0XF0;
P3SEL|=BIT1+BIT3; //在这里如果设置成
}
void Init_CLK(void)
{
unsigned int i;
//将寄存器的内容清零
//XT2震荡器开启
//LFTX1工作在低频模式
//ACLK的分频因子为1
BCSCTL1 = 0X00;
do
{
// 清除OSCFault标志
IFG1 &= ~OFIFG;
for (i = 0x20; i > 0; i--);
}
while ((IFG1 & OFIFG) == OFIFG); // 如果OSCFault =1
//open XT2, LFTX2 选择低频率
BCSCTL1 &= ~(XT2OFF + XTS); //BCSCTL1=0X00 功能一样
//DCO Rsel=7(Freq=3200k/25摄氏度)
BCSCTL1 |= RSEL0 + RSEL1 + RSEL2;
BCSCTL1 |= 0x07;
//MCLK的时钟源为TX2CLK,分频因子为1
BCSCTL2 += SELM1;
//SMCLK的时钟源为TX2CLK,分频因子为1
BCSCTL2 += SELS;
}
//对于系统时钟的选择关系到整个程序运行稳定性.
看到很多卖开发板的人将IIC硬件写上去后再去搞个模拟的IIC总线程序. 感觉到有点说不出的感觉. 其实430的IIC不是专用来外扩展FLASH的,而是用来和一些特殊的电路连接,实现功能. 对于MSP430147~149 15X 16X 的芯片内部有48~60K的Flash了还有必要来个模拟的IIC总线时序么.装个UCOS都可以了.开发板要做的事情就是如何做好非模拟IIC程序的设计.更不是为了和C1搞比拼抢占市场.
上面的程序是经过MSP430F1611的测试.程序的大部分来自
下一篇:MSP430F149 定时器
相关文章