cmicrocontrollerrepeatprogram-entry-pointmsp430

Main function's command repetition problem in C


I'm Noh.

I'm trying to control Microcontroller with C code. And I'm using a code I got from GitHub. (Here's the link https://github.com/jwr/msp430_usi_i2c)

I encountered the problem that main function doesn't stop. Even though there's no loop command in main, command flow repeats again and again.

I write command flow like below.

Write 00 → Write AF → Write 00 → Write 05 → Write 45 → Write 40 → Write 03 → Write B0 → Read EE

And you can see the command flow repeats from beginning to end.

Please refer to below code and the communication result.

#include <msp430.h>
#include <stdint.h>
#include "usi_i2c.h"

#include <stdio.h>
#include <stdlib.h>

// Internal state
static uint16_t const *i2c_sequence;
static uint16_t i2c_sequence_length;
static uint8_t *i2c_receive_buffer;
static uint16_t i2c_wakeup_sr_bits;
i2c_state_type i2c_state = I2C_IDLE;

static uint8_t status;

static inline void i2c_prepare_stop();
static inline void i2c_prepare_data_xmit_recv();

void i2c_send_sequence(uint16_t const * sequence,
                       uint16_t sequence_length,
                       uint8_t *received_data,
                       uint16_t wakeup_sr_bits) {
  while(i2c_state != I2C_IDLE); // we can't start another sequence until the current one is done
  while((status==0xEE)|(status==0xEF)) P1OUT |= 0x01;
    P1OUT &= ~0x01;
  i2c_sequence = sequence;
  i2c_sequence_length = sequence_length;
  i2c_receive_buffer = received_data;
  i2c_wakeup_sr_bits = wakeup_sr_bits;
  i2c_state = I2C_START;
  USICTL1 |= USIIFG; // actually start communication
}

static inline void i2c_prepare_stop() {
  USICTL0 |= USIOE; // SDA = output
  USISRL = 0x00;
  USICNT |= 0x01; // Bit counter= 1, SCL high, SDA low
  i2c_state = I2C_STOP;
}

static inline void i2c_prepare_data_xmit_recv() {
  if(i2c_sequence_length == 0) {
    i2c_prepare_stop(); // nothing more to do, prepare to send STOP
  } else {
  if(*i2c_sequence == I2C_RESTART) {
    USICTL0 |= USIOE; // SDA = output
    USISRL = 0xff; // prepare and send a dummy bit, so that SDA is high
    USICNT = (USICNT & 0xE0) | 1;
    i2c_state = I2C_START;
  }
  else if(*i2c_sequence == I2C_READ) {
    USICTL0 &= ~USIOE; // SDA = input
    USICNT = (USICNT & 0xE0) | 8; // Bit counter = 8, RX data
    i2c_state = I2C_RECEIVED_DATA; // next state: Test data and ACK/NACK
  } else { // a write
    (*i2c_sequence >> 8) == 0
    USICTL0 |= USIOE; // SDA = output
    USISRL = (char)(*i2c_sequence); // Load data byte
    USICNT = (USICNT & 0xE0) | 8; // Bit counter = 8, start TX
    i2c_state = I2C_PREPARE_ACKNACK; // next state: prepare to receive data ACK/NACK
  }
  i2c_sequence++;
  i2c_sequence_length--;
  }
}

#ifdef __GNUC__
__attribute__((interrupt(USI_VECTOR)))
#else
#pragma vector = USI_VECTOR
__interrupt
#endif
void USI_TXRX(void)
{
switch(__even_in_range(i2c_state,12)) {
case I2C_IDLE:
break;

case I2C_START: // generate start condition
  USISRL = 0x00;
  USICTL0 |= (USIGE|USIOE);
  USICTL0 &= ~USIGE;
  i2c_prepare_data_xmit_recv();
  break;

case I2C_PREPARE_ACKNACK: // prepare to receive ACK/NACK
  USICTL0 &= ~USIOE; // SDA = input
  USICNT |= 0x01; // Bit counter=1, receive (N)Ack bit into USISRL
  i2c_state = I2C_HANDLE_RXTX; // Go to next state: check ACK/NACK and 
  continue xmitting/receiving if necessary
  break;

case I2C_HANDLE_RXTX: // Process Address Ack/Nack & handle data TX
  if((USISRL & BIT0) != 0) { // did we get a NACK?
  i2c_prepare_stop();
  } else {
  i2c_prepare_data_xmit_recv();
  }
  break;

case I2C_RECEIVED_DATA: // received data, send ACK/NACK
  *i2c_receive_buffer = USISRL;
  i2c_receive_buffer++;
  USICTL0 |= USIOE; // SDA = output
  if(i2c_sequence_length > 0) {
    // If this is not the last byte
    USISRL = 0x00; // ACK
    i2c_state = I2C_HANDLE_RXTX; // Go to next state: data/rcv again
  } else { // last byte: send NACK
    USISRL = 0xff; // NACK
    i2c_state = I2C_PREPARE_STOP; // stop condition is next
  }
  USICNT |= 0x01; // Bit counter = 1, send ACK/NACK bit
  break;

case I2C_PREPARE_STOP: // prepare stop condition
  i2c_prepare_stop(); // prepare stop, go to state 14 next
  break;

case I2C_STOP: // Generate Stop Condition
  USISRL = 0x0FF; // USISRL = 1 to release SDA
  USICTL0 |= USIGE; // Transparent latch enabled
  USICTL0 &= ~(USIGE|USIOE); // Latch/SDA output disabled
  i2c_state = I2C_IDLE; // Reset state machine for next xmt
  if(i2c_wakeup_sr_bits) {
    _bic_SR_register_on_exit(i2c_wakeup_sr_bits); // exit active if prompted to
  }
  break;
  }
  USICTL1 &= ~USIIFG; // Clear pending flag
}

void main(){
  i2c_init(USIDIV_5, USISSEL_2);

  uint16_t PUP[] = {0xEA, 0x00};
  uint16_t PDWN[] = {0xEA, 0x20};

  uint16_t CVOL_PowerUp[] = {0xEA, 0xAF, 0x00};
  uint16_t AMODE_PowerUp[] = {0xEA, 0x05, 0x45};
  uint16_t AMODE_PowerDown[] = {0xEA, 0x05, 0x41};

  uint16_t RDSTAT_Command[] = {0xEA, 0xB0};
  uint16_t RDSTAT_Read[] = {0xEB, I2C_READ};

  uint16_t START[] = {0xEA, 0x51};

  uint16_t PLAY[] = {0xEA, 0x40, 0x03};
  uint16_t STOP[] = {0xEA, 0x61};

  i2c_send_sequence(PUP, 2, &status, LPM0_bits); // 00

  i2c_send_sequence(CVOL_PowerUp, 3, &status, LPM0_bits); // AF 00
  i2c_send_sequence(AMODE_PowerUp, 3, &status, LPM0_bits); // 05 45
  i2c_send_sequence(PLAY, 3, &status, LPM0_bits); // 40 03

  i2c_send_sequence(RDSTAT_Command, 2, &status, LPM0_bits); // B0
  i2c_send_sequence(RDSTAT_Read, 2, &status, LPM0_bits);
}

void i2c_init(uint16_t usi_clock_divider, uint16_t usi_clock_source) {
  _disable_interrupts();
  USICTL0 = USIPE6|USIPE7|USIMST|USISWRST; // Port & USI mode setup
  USICTL1 = USII2C|USIIE; // Enable I2C mode & USI interrupt
  USICKCTL = usi_clock_divider | usi_clock_source | USICKPL;
  USICNT |= USIIFGCC; // Disable automatic clear control
  USICTL0 &= ~USISWRST; // Enable USI
  USICTL1 &= ~USIIFG; // Clear pending flag
  _enable_interrupts();

  P1OUT = 0xC0; // P1.6 & P1.7 Pullups, others to 0
  P1REN |= 0xC0; // P1.6 & P1.7 Pullups
  P1DIR = 0xFF; // Unused pins as outputs
  P2OUT = 0;
  P2DIR = 0xFF;
}

This is code run result gotten by using Beagle(communication data recorder), and I believe you can see command flow is repeating (from 'Write 00' to 'Read EE')

I have no idea why main function repeats without any loop function.

I want this code run main function's command flow only once.

Do you happen to know why this happens and how to resolve this problem? 😥

Thank you!

Sincerely Noh


Solution

  • Most programs for a microcontroller have some assembly language initialisation code that does some basic configuration of the MCU, copies the data section from flash to RAM, zeros the .bss, then jumps to main().

    Typically, main() would never return. It would consist of an infinite loop, doing whatever tasks the software is meant to do, forever, until power is removed.

    So your code returning is not very typical. What is the MCU meant to do if main() returns? It has nothing else to run. Some options for it might be to:

    1. Spin doing nothing
    2. Call main() again (in a loop)
    3. Reset the MCU, which effectively restarts your code

    It sounds like, in your case, either 2 or 3 is happening.

    If you want your code to just stop after it has done its thing, add an empty infinite loop at the end of main():

    for (;;)
        ;