This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
I'm new to this so I may have done something stupid. When I run my code, the msp430 (master) does an extra byte read instead of sending the stop code. I'm attaching the logic analyzer. Thanks!
code (I bolded the parts I thought relevant):
#include <msp430.h>
#include "gy-80_config.h"
int RXByteCtr, RPT_Flag = 0; // enables repeated start when 1
volatile unsigned char RxBuffer[128]; // Allocate 128 byte of RAM
unsigned char TxData; // TX data
unsigned char *PRxData; // Pointer to RX data
unsigned char TXByteCtr, RX = 0;
void Setup_TX(unsigned char );
void Setup_RX(unsigned char );
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
P1SEL |= BIT6 + BIT7; // Assign I2C pins to USCI_B0
P1SEL2|= BIT6 + BIT7; // Assign I2C pins to USCI_B0
while(1){
//Transmit process
Setup_TX(ACCE_ADDR);
TXByteCtr = 1;
TxData = ACCE_DEVID;
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
//Receive process
Setup_RX(ACCE_ADDR);
PRxData = (unsigned char *)RxBuffer; // Start of RX buffer
RXByteCtr = 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
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 Receive?
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 = TxData; // Load TX buffer
TXByteCtr--; // Decrement TX byte counter
}
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(unsigned char slAddress){
_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 = slAddress; // Slave Address
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
IE2 |= UCB0TXIE; // Enable TX interrupt
}
void Setup_RX(unsigned char slAddress){
_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 = slAddress; // Slave Address
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
IE2 |= UCB0RXIE; // Enable RX interrupt
}
OK, so I know this ISR is designed for 2+ byte read, but how do I adapt it to just read one byte if desired? I gather it has something to do with the stop condition being set too late and thus triggering a follow-up interrupt?
Unrelated, but sometimes I get stuck in LPM0 with no interrupts to get out of it. When I unplug the peripheral and plug it back in and reset the msp430 it works (typically). Is this a hardware thing? I have 10k pullups on scl and sda..
This isn't possible when workign interrupt-driven.Scott Richards said:how do I adapt it to just read one byte if desired?
Unless you get a NACK, the first itnerrupt you get after settign TXSTT is teh RXIFG interrupt. At this point, the USCI has already started receiving the next byte. If you set TXSTP now, the next byte will be received before sending stop. If you set TXSTP right after setting TXSTT, no byte will be received. You'll have to set TXSTP while the USCI is still reading the first byte, but you won't get any interrupt here. YOu could setup a tiemr to give you an interrupt mid-byte (you know the baudrate). But if the slave uses clock stretching during the start byte (some do, when not yet ready for serving the request), this timing will be wrong.
The only safe way to receive exactly one byte is to not use interrupts and do a busy-waiting for TXSTT getting cleared. At this moment, you can set TXSTP to receive just one byte (which then my be received by interrupt, so you don't have to wait for the complete reception).
Maybe 10k are not enough. You may try 4k7. It depends on the peripherals, their logical voltage thresholds etc.Scott Richards said:Unrelated, but sometimes I get stuck in LPM0 with no interrupts to get out of it. When I unplug the peripheral and plug it back in and reset the msp430 it works (typically). Is this a hardware thing? I have 10k pullups on scl and sda..
Ahhhhhh, that makes sense. I guess I'll just ditch the interrupts and block through things. Thanks!
**Attention** This is a public forum