OK so I'm using MSP430FR5738 to configure Si4705 FM radio receiver. I need to send commands of multiple bytes through I2C within the same start-stop period, plus reading back a single byte, in master mode. The byte length varies based on command, but right now I'm just sending a single 3-byte "power-up" command. What I do is storing those commands in arrays, and send them to the UCB0TXBUF register through an "intermediate buffer array" using I2C interrupt service. The STOP condition is generated by software in ISR. Only NACK, TX0, RX0 interrupts are enabled.
The step-by-step debug run (I'm using Code Composer Studio + MSPFET with spy-by-wire connection to FR5738) shows that the program is unable to enter interrupt service routine, while everything else works normal. The UCB IE, IFG, IV registers all show correct values with interrupt flag + interrupt signals set, but it just refuse to enter ISR and load UCB0 TX buffer...
And I've checked everything on PCB too, no problem found. It looks like a software problem.
By the way I've also been trying with DMA. But it seems the compiler put the next "command" array element (static unsigned short) in +2 memory address, while DMA address increments by only 1... and I didn't get the correct result either.?!
The code is attached here. Any idea???
#include <msp430.h>
unsigned short status[1]; // holds the single received byte
unsigned short buffer[8]; // for I2C multiple byte handling
unsigned short count;
unsigned short length;
void i2cinit(void);
void i2ctx (unsigned short*, unsigned short);
void i2crx1byte (void);
// unsigned short i2crx (unsigned short*, unsigned short);
int main(void)
{
// load I2C commands in arrays of unsigned short type
// CTS interrupt EN, GPO2 out EN, boot normally, RCLK in, transmit, digital audio in
// response = status
static unsigned short power_up[3] = {0x0001, 0x00C0, 0x0005};
// code starts here......
WDTCTL = WDTPW+WDTHOLD; // Stop WDT
// for testing purpose only
// enable LED output
P1OUT &= ~BIT5; // yellow LED @ P1.5, for debugging only...
P2OUT &= ~0x03; // green/red LED @ P2.0 & P2.1
PJOUT &= ~BIT5; // Clear PJ.5, connect to RST pin of FM chip
P1DIR |= BIT5; // set to output direction
P2DIR |= 0x03;
PJDIR |= BIT5;
PM5CTL0 &= ~LOCKLPM5; // disable low power mode
// LED config end
// select pins for I2C
P1SEL1 |= BIT6 + BIT7;
//configure 8MHz DCO for CPU clock, divide by 4 for SMCLK
CSCTL0 = CSKEY; // Unlock CS registers, password = 0xA5
CSCTL1 = DCOFSEL_3; // Set DCO to 8MHz
CSCTL2 = SELS__DCOCLK + SELM__DCOCLK;// Set SMCLK = DCO; MCLK = DCO
CSCTL3 = DIVS__4 + DIVM__1; // MCLK = 16MHz, SMCLK = 2MHz
CSCTL6 |= MCLKREQEN + SMCLKREQEN + ACLKREQEN; // enable clock requests
CSCTL0_H = 0; // lock CS register
PJOUT |= BIT5; // first, RST pin of FM chip = high
i2cinit(); // initialize I2C
// yellow LED @ P1.5 on, for testing purpose only
P1OUT |= BIT5;
P2OUT |= BIT0;
i2ctx(power_up, 3);
i2crx1byte();
if (status[0] != 0x0080)
{
P2OUT &= ~BIT0; // turn off LED
}
return 0;
}
// function definition: I2C transmit, of one full command sequence with "size"
void i2ctx (unsigned short *command, unsigned short size)
{
unsigned short noob = 0;
// fill the buffer array...
count = size;
length = size;
while (count != 0)
{
count--;
buffer[count] = command[count];
}
// set transmit mode
UCB0CTLW0 |= UCTR;
// enable Tx and non-acknowledge interrupt
UCB0IE |= UCTXIE0 + UCNACKIE;
// start transmission
UCB0CTLW0 |= UCTXSTT;
// this loop makes sure CPU stays here before Tx finish...
while (UCB0IFG & UCBCNTIFG == 0)
{
noob = noob;
}
P2OUT &= ~BIT1; // LED @ P2.0 back to green after successful resend
P2OUT |= BIT0;
}
// receive a single byte
void i2crx1byte (void)
{
unsigned short noob = 0;
count = 0;
// set receive mode
UCB0CTLW0 &= ~UCTR;
// enable receive and non-acknowledge interrupt
UCB0IE |= UCRXIE0 + UCNACKIE;
// start transmission and stop, same time...
UCB0CTLW0 |= UCTXSTT + UCTXSTP;
// loop makes sure CPU stays here before stop...
while (UCB0IFG & UCRXIFG0 == 0)
{
noob = noob;
}
status[0] = buffer[0];
P2OUT &= ~BIT1; // LED @ P2.0 back to green after successful resend
P2OUT |= BIT0;
}
// interrupt service routine of I2C
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCIB0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCIB0_ISR (void)
#else
#error Compiler not supported!
#endif
{
switch(__even_in_range(UCB0IV,0x1E))
{
case 0x00: break; // Vector 0: No interrupts break;
case 0x04: // non-acknowledge
UCB0CTLW0 |= UCTXSTP; // stop I2C
count = 0;
UCB0CTLW0 &= ~UCTXNACK; // clear interrupt flag
P2OUT &= ~BIT0; // LED turns red
P2OUT |= BIT2;
UCB0CTLW0 |= UCTXSTT; // start after stop
break; // Vector 4: NACKIFG break;
// to cope with FM chip, cannot use START without STOP first...
case 0x16: // receive 0 ready
buffer[count] = UCB0RXBUF; // move data from buffer
count++;
break;
case 0x18: // transmit 0 ready
UCB0TXBUF = buffer[count]; // move data from buffer
count++;
if (count >= length)
UCB0CTLW0 |= UCTXSTP; // software stop once size is reached
break;
default: break;
}
}
// initialize I2C
void i2cinit(void)
{
// enable config
UCB0CTLW0 = UCSWRST;
// I2C master mode, SMCLK, sync
UCB0CTLW0 |= UCMST + UCMODE_3 + UCSYNC + UCSSEL__SMCLK;
// de-glitch time 25ns
UCB0CTLW1 |= UCGLIT_1;
// I2C clock is SMCLK divided by 28
UCB0BRW = 0x001C;
// slave address, 7-bit, 0010001
UCB0I2CSA = 0x11;
// end config
UCB0CTLW0 &=~ UCSWRST;
}