你好:
我的问题如下:
用2553通过I2C写一个数字功放,
#ifndef SIMPLE_I2C_H
#define SIMPLE_I2C_H
#endif
#define uchar unsigned char
uchar TRFlag;
uchar DataFlag=0;
uchar TxData[2];
uchar *PTxData;
//uchar Rxdata;
uchar Txctr;
uchar I2C_buf1;
void init_i2c(void);
void I2Cm_Tx(uchar valueReg,uchar RegAddress,uchar DeviceAddress);
void I2Cm_Rx(uchar valueReg,uchar RegAddress,uchar DeviceAddress);
#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void){
if(TRFlag==1){
if(Txctr){
Txctr–;
UCB0TXBUF=*PTxData++; //no data transmit
__bic_SR_register_on_exit(CPUOFF); // get out of interrupt
}
else{
UCB0CTL1|=UCTXSTP;
IFG2&=~UCB0TXIFG;
__bic_SR_register_on_exit(CPUOFF);
}
}
else{
I2C_buf1=UCB0RXBUF;
UCB0CTL1|=UCTXSTP;
IFG2&=~UCB0RXIFG;
__bic_SR_register_on_exit(CPUOFF);
}
}
void I2Cm_Tx(uchar valueReg,uchar RegAddress,uchar DeviceAddress){
_DINT();
TRFlag=1;
if(DataFlag==1){
Txctr=1;
DataFlag=0;
}
else{
Txctr=2;
}
IE2&=~UCB0RXIE;
while(UCB0CTL1&UCTXSTP);
UCB0CTL1|=UCSWRST;
UCB0CTL0=UCMST+UCMODE_3+UCSYNC;
UCB0CTL1=UCSSEL_2+UCSWRST;
UCB0BR0=12;
UCB0BR1=0;
UCB0I2CSA=DeviceAddress;
UCB0CTL1&=~UCSWRST;
IE2|=UCB0TXIE;
TxData[0]=RegAddress;
TxData[1]=valueReg;
PTxData=TxData;
while(UCB0CTL1&UCTXSTP);
UCB0CTL1|=UCTR+UCTXSTT;
__bis_SR_register(CPUOFF+GIE); //enter interrupt
while(UCB0CTL1&UCTXSTP); //return to this
}
void I2Cm_Rx(uchar valueReg,uchar RegAddress,uchar DeviceAddress){
_DINT();
uchar temp_dev,temp_reg;
temp_dev=DeviceAddress;
temp_reg=RegAddress;
TRFlag=0;
DataFlag=1;
I2Cm_Tx(0x00,temp_reg,temp_dev);
IE2&=~UCB0TXIE;
while(UCB0CTL1&UCTXSTP);
UCB0CTL1|=UCSWRST;
UCB0CTL0=UCMST+UCMODE_3+UCSYNC;
UCB0CTL1=UCSSEL_2+UCSWRST;
UCB0BR0=12;
UCB0BR1=0;
UCB0I2CSA=temp_dev;
UCB0CTL1&=~UCSWRST;
IE2|=UCB0RXIE;
while(UCB0CTL1&UCTXSTP);
UCB0CTL1|=UCTXSTT;
__bis_SR_register(CPUOFF+GIE);
while(UCB0CTL1&UCTXSTP);
}
void init_i2c(void){
P1SEL|=BIT6+BIT7;
P1SEL2|=BIT6+BIT7;
}
调用I2Cm_TX()后,只执行完if(Txctr){}一遍后跳出中断,捕捉到下面的波形:
I2C_SA是0X38,上面的数据不对吧?请帮忙分析,谢谢!
Xutong Han2:
先用TI官方的程序调试一下,排除硬件问题,再去调试修改软件~
Triton Zhang:
回复 Xutong Han2:
波形是错的,波形发出来的数据时0XE1.
1231:
回复 Xutong Han2:
那我改的这个对不对?这个对你们来说不是很简单的吗?
1231:
回复 Triton Zhang:
这能称为数据吗?I2C数据十几毫秒,别乱忽悠
Triton Zhang:
回复 1231:
你用那个眼睛看到的I2C数据不能是几十毫秒? I2C的速率是从直流到100K(高速400K).
从波形图上看数据就是0XE1,如果不会看的话,回去翻书去,别再这里随意下结论。如果你有自己的分析,欢迎贴上来。
至于说到这个波形的问题,那是很多的,但不是说就没数据。
1. 按照I2C的标准,SCL和SDA在不使用的时候都应该是高电平,这个图中的SCL是低电平。
2. START的位置不是很明显,但可能是示波器没展开造成的。
3. 第5个CLK宽度太大,什么原因造成的不清楚,但还是一个合格I2C CLK波形
4. 第6,7,8个CLK被遮住了,但还是能看出是CLK,
5. 8个数据位之后没有STOP
这是波形分析,至于程序有什么问题,没时间去细究。
1231:
回复 Triton Zhang:
我是不懂,所以才问,你们回复个看官方例程算什么,我是看了才改的,硬件没问题,之前用别的MCU写I2C也OK,你没时间就别回复啊,那些代码需要你们很多时间细究吗?以前有帖子质疑你们的官方例程咋不回复,没那耐心干嘛开在线支持?说你们忽悠就急了
Hardy Hu:
你好:
主函数那一段能发给大家看看吗?
还有就是注意看START和STOP信号,分析程序的执行结果。
Regards,
Hardy
Triton Zhang:
回复 1231:
首先回你例程的不是我,从你的代码中我看到了无数例程的影子。但是,我也没觉得回你例程有什么不妥。 你有没有认真地去读下例程呢?有没有去挨个看例程中的语句操作寄存器的含义呢? 你在编写代码之前,有没有实际的画一下流程图,然后去检查下里面的逻辑错误呢? 如果你要人家帮忙看代码,至少要把你代码贴完整,代码中的注释写清楚。你的代码中的各个变量的含义是什么?如果可能,最好贴上你的流程分析。否者人家要来猜你的意图,如果程序里面有逻辑错误,看起来就更吃力,你觉得这个不花时间行吗? 即使是一个很小的代码,不同的程序员思路就不一样,写出来的流程和代码都不一样,你不要只站在你的角度看代码,觉得人家花不了时间。
这里就以你的代码为例子,把我浏览你代码的大致想法简单分析一下,你可以看到一个逻辑混乱的代码给看的人带来多大的困难。真的不如自己去推翻重新写一遍。 1. 代码里没有MAIN函数,程序的入口进行了什么初始化看不到。 2. 主频设置的多大,SMCLK,ACLK是用的什么时钟源都不清楚,这关系到如何判断I2C的波特率。 没有这些信息,不要说你的I2C的波形是20多个毫秒,就算是1S都是可能的.这就是为什么你觉得10几毫秒的I2C数据不能,因为你从例程直接抄过来的,认为就是100K的速率,而看代码的人根本没得到这些信息。 3. I2C的初始化就只初始化了下管脚,你把I2C的初始化放在了I2C的发送和接收的代码中,实在是不知道咋有这样的想法。唯一的解释就是你从TI的发射例程中拷贝了一段,然后又从接收例程中拷贝了一段。 3. 从代码来猜TRFlag标志是用来标志I2C发送还是接受状态的,这个标志在USCIAB0TX_ISR的中断出来函数中用来判断是接收还是发射状态。这个标志在你I2Cm_RX的函数入口被初始化成0,按照流程走到调用I2Cm_TX()中又被置1,调用返回后 这个标志就一直保留成1了.换句话说在I2Cm_RX()函数的头半部分TRFlag =0, 后半部分TRFlag =1. 这么混乱的逻辑,你让谁能一眼看出来你想干什么? 4. 回到开始的话题,按照一般的理解,I2Cm_RX(),I2Cm_TX()这样命名的函数应该是对等的平行关系函数,而在你的代码中I2Cm_RX()又在调用I2Cm_TX,真不晓得你在编程前是否真正的去分析过系统和流程没有.另外推荐程序员在编程之前学习下程序命令规则,这个一定会给你带来好处的. 5. 继续谈问题,在中断函数中,你通过TRFlag标志来判断是接收中断还是发射中断,问题是MSP430G2553的I2C中断向量有2个,一个发射,一个接收,你在发射中断里去判断接收的数据中断,如何能有结果? 这说明你看例程都没仔细看,更不用说看数据手册了。 6. 继续,DataFlag标志,从你I2Cm_Tx的函数中来猜,你是想用这个来区分只传送设备地址和设备地址+寄存器地址。在你的I2Cm_RX()函数中设置了这个标志然后调用I2Cm_TX去发送设备地址+寄存器地址.问题是之后你连发送地址+读操作都没有, 就直接切换到接收模式。你看到那个I2C的协议可以这样做的? 这说明你连I2C的协议都没去认真阅读。 7. 在处理I2C的中断事件中,把第一次进TX中断当做是START信号的中断事件,这样做风险很大而且也很让人不好理解。 你要去实际判断是否有START信号的中断,这个状态在UCBxSTAT的UCSTTIFG来表示。MSP430G系列的I2C寄存器总共就只有12个, 你在编程前花不了一个小时就浏览完了,比你出了问题,盲人摸象地去找问题花的时间少多了. D8. 纵观全部代码,基本上就是把TI的例程按照你的思路去揉在一起,这样的代码风险非常大。既没有防冲突能力又没有异常处理机制,很多地方都存在死循环的可能性。
我不是说一定要挑剔你代码的毛病,你想一下,如果一眼能看到这么多问题,逻辑思路一点都不清晰的代码,你还有兴趣去挨个检查问题么?这不是说给你找一两个问题就能解决的聊的. 根本问题在于你在设计代码的时候,没有花时间去了解相关知识,没有去分析协议,没有去读数据手册,没有认真分析流程画出流程及逻辑关系。这些都不是能力问题,是你做事的方式的问题。 任何人都是从无知到有知的,如果你总想让人家来帮你找问题,而自己不去反省自己的问题,怎么能成为一个合格的软件工程师呢?
1231:
回复 Triton Zhang:
首先感谢你的精彩分析和中肯建议,虽然我不是软件工程师,但这代码确实太烂,并对自己的无理表示道歉。
1.这个程序用于2553做主控的蓝牙音箱中,用TI的数字功放,main函数功能主要有四个:a.硬件的初始化(不包括功放);b.功放初始化;c.按键操作;d.显示控制。
用I2C来实现对功放的初始化,我把功能分布调试,所以没有贴main(),初始化数据在另一个文件中,这段代码是从官方的例程12抄过来的,所以没加注释。
2.功放初始化:a.对功放指定寄存器写数据;b.读出指定寄存器值计算后重新写入该寄存器
(读操作先写入被读寄存器地址,再进行当前地址寄存器读取,所以在RX内先调用了TX)(TX\RX每次只完成单个字节数据(不包括地址)的读写)
3.RX、TX内每次进行调用都初始化I2C,这是对应例程i2c_12中的tx、rx set_up,按照我对UCSWRST的理解,I2C初始化应该和IO一起,初始化一遍即可,把SA放到TX、RX函数中。不是一定要照抄例程,只是觉得刚开始还是尽量接近官方例程。
4.在DS中,如果没理解错,T\RX flag共用一个向量(Status flag共用一个向量),参照例程,我还是把他们放一起。
5.注释一下我贴的代码
#ifndef SIMPLE_I2C_H#define SIMPLE_I2C_H#endif#define uchar unsigned char
uchar TRFlag; //判断是发送还是接收中断,1->txuchar DataFlag=0; //判断是只发送功放寄存器地址还是需发数据,0->需加数据uchar TxData[2]; //装寄存器地址或者+数据uchar *PTxData; //指向上面的数组//uchar Rxdata;uchar Txctr; //发送计数uchar I2C_buf1; //存储要发送的或者接收的I2C数据
void init_i2c(void);void I2Cm_Tx(uchar valueReg,uchar RegAddress,uchar DeviceAddress); //变量:数据I2C_buf1、寄存器地址、功放地址void I2Cm_Rx(uchar valueReg,uchar RegAddress,uchar DeviceAddress);
#pragma vector=USCIAB0TX_VECTOR__interrupt void USCIAB0TX_ISR(void){ if(TRFlag==1){ //TRFlag、Txctr在TX建立过程中赋值 if(Txctr){ Txctr–; UCB0TXBUF=*PTxData++; __bic_SR_register_on_exit(CPUOFF); // 按照对例程的理解,这句应该不要,STP不发,不断中断才会进行完Txctr-到0,再到下面给STP,按照例程中
recieve()\transmit(), 我在每次调用TX RX中断返回后都while 已STP发送,这样这里需要加这一句跳出低功耗,不加不会循环不
会发STP,加了更不会循环 } else{ //实际这里没有执行到 UCB0CTL1|=UCTXSTP; IFG2&=~UCB0TXIFG; __bic_SR_register_on_exit(CPUOFF); } } else{ I2C_buf1=UCB0RXBUF; UCB0CTL1|=UCTXSTP; IFG2&=~UCB0RXIFG; __bic_SR_register_on_exit(CPUOFF); }}
void I2Cm_Tx(uchar valueReg,uchar RegAddress,uchar DeviceAddress){ _DINT(); TRFlag=1; //发送标志 if(DataFlag==1){ //RX函数内部DataFlag置1后紧接着调用TX,如通过RX间接调用TX,DataFlag马上清0,避免影响TX后续的直接调用 Txctr=1; DataFlag=0; } else{ Txctr=2; } IE2&=~UCB0RXIE; //初始化 while(UCB0CTL1&UCTXSTP); UCB0CTL1|=UCSWRST; UCB0CTL0=UCMST+UCMODE_3+UCSYNC; UCB0CTL1=UCSSEL_2+UCSWRST; //选择SMCLK12分频为时钟,例程中未初始化主时钟,默认S/MCLK为800K,实际不超过SCL不超过67K
,我写的main中也为800K UCB0BR0=12; UCB0BR1=0; UCB0I2CSA=DeviceAddress; UCB0CTL1&=~UCSWRST; IE2|=UCB0TXIE; TxData[0]=RegAddress; TxData[1]=valueReg; //通过RX间接调用TX时,此值为I2C_buf1,但不发送 PTxData=TxData; //指针赋值 while(UCB0CTL1&UCTXSTP); UCB0CTL1|=UCTR+UCTXSTT; __bis_SR_register(CPUOFF+GIE); //enter interrupt while(UCB0CTL1&UCTXSTP); //return to this}
void I2Cm_Rx(uchar valueReg,uchar RegAddress,uchar DeviceAddress){ _DINT(); uchar temp_dev,temp_reg; //RX TX参数传递 temp_dev=DeviceAddress; temp_reg=RegAddress; TRFlag=0; //接收标志,这个应该放在调用TX后 DataFlag=1; I2Cm_Tx(0x00,temp_reg,temp_dev); IE2&=~UCB0TXIE; //初始化 while(UCB0CTL1&UCTXSTP); UCB0CTL1|=UCSWRST; UCB0CTL0=UCMST+UCMODE_3+UCSYNC; UCB0CTL1=UCSSEL_2+UCSWRST; UCB0BR0=12; UCB0BR1=0; UCB0I2CSA=temp_dev; UCB0CTL1&=~UCSWRST; IE2|=UCB0RXIE; while(UCB0CTL1&UCTXSTP); UCB0CTL1|=UCTXSTT; __bis_SR_register(CPUOFF+GIE); while(UCB0CTL1&UCTXSTP);}
void init_i2c(void){ P1SEL|=BIT6+BIT7; P1SEL2|=BIT6+BIT7;}
6.参考的官方例程
//******************************************************************************// MSP430G2xx3 Demo – USCI_B0 I2C Master TX/RX multiple bytes from MSP430 Slave// with a repeated start in between TX and RX operations.//// Description: This demo connects two MSP430's via the I2C bus. The master// transmits to the slave, then a repeated start is generated followed by a // receive operation. This is the master code. This code demonstrates how to // implement an I2C repeated start with the USCI module using the USCI_B0 TX // interrupt.// ACLK = n/a, MCLK = SMCLK = BRCLK = default DCO = ~1.2MHz//// ***to be used with msp430x22x4_uscib0_i2c_13.c***//// /|\ /|\// MSP430F24x 10k 10k MSP430G2xx3// slave | | master// —————– | | —————–// -|XIN P3.1/UCB0SDA|<-|—+->|P3.1/UCB0SDA XIN|-// | | | | |// -|XOUT | | | XOUT|-// | P3.2/UCB0SCL|<-+—–>|P3.2/UCB0SCL |// | | | |//// D. Dang// Texas Instruments Inc.// February 2011// Built with CCS Version 4.2.0 and IAR Embedded Workbench Version: 5.10//******************************************************************************#include "msp430g2553.h"#define NUM_BYTES_TX 3 // How many bytes?#define NUM_BYTES_RX 2int RXByteCtr, RPT_Flag = 0; // enables repeated start when 1volatile unsigned char RxBuffer[128]; // Allocate 128 byte of RAMunsigned char *PTxData; // Pointer to TX dataunsigned char *PRxData; // Pointer to RX dataunsigned char TXByteCtr, RX = 0;unsigned char MSData = 0x55;void Setup_TX(void);void Setup_RX(void);void Transmit(void);void Receive(void);void main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop WDT P1SEL |= BIT6 + BIT7; // Assign I2C pins to USCI_B0 P1SEL2|= BIT6 + BIT7; // Assign I2C pins to USCI_B0 while(1){ //Transmit process Setup_TX(); RPT_Flag = 1; Transmit(); while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent //Receive process Setup_RX(); Receive(); while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent }}//——————————————————————————-// The USCI_B0 data ISR is used to move received data from the I2C slave// to the MSP430 memory. It is structured such that it can be used to receive// any 2+ number of bytes by pre-loading RXByteCtr with the byte count.//——————————————————————————-#pragma vector = USCIAB0TX_VECTOR__interrupt void USCIAB0TX_ISR(void){ if(RX == 1){ // Master Recieve? RXByteCtr–; // Decrement RX byte counter if (RXByteCtr) { *PRxData++ = UCB0RXBUF; // Move RX data to address PRxData } else { if(RPT_Flag == 0) UCB0CTL1 |= UCTXSTP; // No Repeated Start: stop condition if(RPT_Flag == 1){ // if Repeated Start: do nothing RPT_Flag = 0; } *PRxData = UCB0RXBUF; // Move final RX data to PRxData __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 }} else{ // Master Transmit if (TXByteCtr) // Check TX byte counter { UCB0TXBUF = MSData++; // Load TX buffer TXByteCtr–; // Decrement TX byte counter } else { if(RPT_Flag == 1){ RPT_Flag = 0; PTxData = &MSData; // TX array start address TXByteCtr = NUM_BYTES_TX; // Load TX byte counter __bic_SR_register_on_exit(CPUOFF); } else{ UCB0CTL1 |= UCTXSTP; // I2C stop condition IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } } } }void Setup_TX(void){ _DINT(); RX = 0; IE2 &= ~UCB0RXIE; while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent// Disable RX interrupt UCB0CTL1 |= UCSWRST; // Enable SW reset UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz UCB0BR1 = 0; UCB0I2CSA = 0x48; // Slave Address is 048h UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation IE2 |= UCB0TXIE; // Enable TX interrupt}void Setup_RX(void){ _DINT(); RX = 1; IE2 &= ~UCB0TXIE; UCB0CTL1 |= UCSWRST; // Enable SW reset UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz UCB0BR1 = 0; UCB0I2CSA = 0x48; // Slave Address is 048h UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation IE2 |= UCB0RXIE; // Enable RX interrupt}void Transmit(void){ PTxData = &MSData; // TX array start address TXByteCtr = NUM_BYTES_TX; // Load TX byte counter while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts}void Receive(void){ PRxData = (unsigned char *)RxBuffer; // Start of RX buffer RXByteCtr = NUM_BYTES_RX-1; // Load RX byte counter while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent UCB0CTL1 |= UCTXSTT; // I2C start condition __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts}
1231:
回复 Triton Zhang:
感谢你的精彩分析和中肯建议,虽然不是软件工程师,但这代码确实很烂,并对自己的无理表示道歉。